From 4a0f4b357834a55d4c59482b264fd68f901d7cb9 Mon Sep 17 00:00:00 2001 From: Facundo Spagnuolo Date: Thu, 25 Apr 2019 19:44:09 -0300 Subject: [PATCH] Payroll: Update tests to using a real denomination token --- future-apps/payroll/contracts/Payroll.sol | 2 +- .../contracts/Payroll_add_employee.test.js | 27 +-- .../contracts/Payroll_allowed_tokens.test.js | 56 +++-- .../test/contracts/Payroll_bonuses.test.js | 91 ++++---- .../test/contracts/Payroll_forwarding.test.js | 29 ++- .../test/contracts/Payroll_gas_costs.test.js | 65 +++--- .../contracts/Payroll_get_employee.test.js | 29 ++- .../test/contracts/Payroll_initialize.test.js | 28 +-- .../contracts/Payroll_modify_employee.test.js | 28 +-- .../test/contracts/Payroll_payday.test.js | 195 +++++++++--------- .../test/contracts/Payroll_rates.test.js | 37 +--- .../test/contracts/Payroll_reentrancy.test.js | 8 +- .../contracts/Payroll_reimbursements.test.js | 87 ++++---- .../test/contracts/Payroll_settings.test.js | 21 +- .../Payroll_terminate_employee.test.js | 46 ++--- .../Payroll_token_allocations.test.js | 47 ++--- future-apps/payroll/test/helpers/deploy.js | 3 +- future-apps/payroll/test/helpers/numbers.js | 17 +- .../payroll/test/helpers/set_token_rates.js | 23 --- future-apps/payroll/test/helpers/time.js | 18 ++ future-apps/payroll/test/helpers/tokens.js | 62 ++++++ 21 files changed, 437 insertions(+), 482 deletions(-) delete mode 100644 future-apps/payroll/test/helpers/set_token_rates.js create mode 100644 future-apps/payroll/test/helpers/time.js create mode 100644 future-apps/payroll/test/helpers/tokens.js diff --git a/future-apps/payroll/contracts/Payroll.sol b/future-apps/payroll/contracts/Payroll.sol index 98a6f9f801..bd20d595cd 100644 --- a/future-apps/payroll/contracts/Payroll.sol +++ b/future-apps/payroll/contracts/Payroll.sol @@ -665,7 +665,7 @@ contract Payroll is EtherTokenConstant, IForwarder, IsContract, AragonApp { return ONE; } - (uint128 xrt, uint64 when) = feed.get(denominationToken, _token); + (uint128 xrt, uint64 when) = feed.get(_token, denominationToken); // Check the price feed is recent enough if (getTimestamp64().sub(when) >= rateExpiryTime) { diff --git a/future-apps/payroll/test/contracts/Payroll_add_employee.test.js b/future-apps/payroll/test/contracts/Payroll_add_employee.test.js index f0f4460723..9101ce2450 100644 --- a/future-apps/payroll/test/contracts/Payroll_add_employee.test.js +++ b/future-apps/payroll/test/contracts/Payroll_add_employee.test.js @@ -1,23 +1,18 @@ const { assertRevert } = require('@aragon/test-helpers/assertThrow') const { getEvents, getEventArgument } = require('../helpers/events') -const { maxUint64, annualSalaryPerSecond } = require('../helpers/numbers')(web3) -const { deployErc20TokenAndDeposit, deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { MAX_UINT64, annualSalaryPerSecond } = require('../helpers/numbers')(web3) +const { USD, deployDAI } = require('../helpers/tokens.js')(artifacts, web3) +const { NOW, TWO_MONTHS, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' contract('Payroll employees addition', ([owner, employee, anotherEmployee, anyone]) => { - let dao, payroll, payrollBase, finance, vault, priceFeed, denominationToken - - const NOW = 1553703809 // random fixed timestamp in seconds - const ONE_MONTH = 60 * 60 * 24 * 31 - const TWO_MONTHS = ONE_MONTH * 2 - const RATE_EXPIRATION_TIME = TWO_MONTHS - - const TOKEN_DECIMALS = 18 + let dao, payroll, payrollBase, finance, vault, priceFeed, DAI before('deploy base apps and tokens', async () => { ({ dao, finance, vault, payrollBase } = await deployContracts(owner)) - denominationToken = await deployErc20TokenAndDeposit(owner, finance, 'Denomination Token', TOKEN_DECIMALS) + DAI = await deployDAI(owner, finance) }) beforeEach('create payroll and price feed instance', async () => { @@ -26,11 +21,11 @@ contract('Payroll employees addition', ([owner, employee, anotherEmployee, anyon describe('addEmployee', () => { const role = 'Boss' - const salary = annualSalaryPerSecond(100000, TOKEN_DECIMALS) + const salary = annualSalaryPerSecond(100000) context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the sender has permissions to add employees', () => { @@ -69,7 +64,7 @@ contract('Payroll employees addition', ([owner, employee, anotherEmployee, anyon it('can add another employee', async () => { const anotherRole = 'Manager' - const anotherSalary = annualSalaryPerSecond(120000, TOKEN_DECIMALS) + const anotherSalary = annualSalaryPerSecond(120000) const receipt = await payroll.addEmployee(anotherEmployee, anotherSalary, anotherRole, startDate) const anotherEmployeeId = getEventArgument(receipt, 'AddEmployee', 'employeeId') @@ -91,7 +86,7 @@ contract('Payroll employees addition', ([owner, employee, anotherEmployee, anyon assert.equal(accruedSalary, 0, 'employee accrued salary does not match') assert.equal(employeeSalary.toString(), anotherSalary.toString(), 'employee salary does not match') assert.equal(lastPayroll.toString(), startDate.toString(), 'employee last payroll does not match') - assert.equal(endDate.toString(), maxUint64(), 'employee end date does not match') + assert.equal(endDate.toString(), MAX_UINT64, 'employee end date does not match') }) }) diff --git a/future-apps/payroll/test/contracts/Payroll_allowed_tokens.test.js b/future-apps/payroll/test/contracts/Payroll_allowed_tokens.test.js index 17c5f412cb..4587770d58 100644 --- a/future-apps/payroll/test/contracts/Payroll_allowed_tokens.test.js +++ b/future-apps/payroll/test/contracts/Payroll_allowed_tokens.test.js @@ -1,25 +1,19 @@ const PAYMENT_TYPES = require('../helpers/payment_types') -const setTokenRates = require('../helpers/set_token_rates')(web3) const { getEvent } = require('../helpers/events') const { assertRevert } = require('@aragon/test-helpers/assertThrow') -const { deployErc20TokenAndDeposit, deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { NOW, ONE_MONTH, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) +const { USD, deployDAI, deployTokenAndDeposit, setTokenRates } = require('../helpers/tokens.js')(artifacts, web3) const MAX_GAS_USED = 6.5e6 const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' contract('Payroll allowed tokens,', ([owner, employee, anyone]) => { - let dao, payroll, payrollBase, finance, vault, priceFeed, denominationToken - - const NOW = 1553703809 // random fixed timestamp in seconds - const ONE_MONTH = 60 * 60 * 24 * 31 - const TWO_MONTHS = ONE_MONTH * 2 - const RATE_EXPIRATION_TIME = TWO_MONTHS - - const TOKEN_DECIMALS = 18 + let dao, payroll, payrollBase, finance, vault, priceFeed, DAI before('deploy base apps and tokens', async () => { ({ dao, finance, vault, payrollBase } = await deployContracts(owner)) - denominationToken = await deployErc20TokenAndDeposit(owner, finance, 'Denomination Token', TOKEN_DECIMALS) + DAI = await deployDAI(owner, finance) }) beforeEach('create payroll and price feed instance', async () => { @@ -28,8 +22,8 @@ contract('Payroll allowed tokens,', ([owner, employee, anyone]) => { describe('addAllowedToken', () => { context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the sender has permissions', () => { @@ -37,13 +31,13 @@ contract('Payroll allowed tokens,', ([owner, employee, anyone]) => { context('when it does not reach the maximum amount allowed', () => { it('can allow a token', async () => { - const receipt = await payroll.addAllowedToken(denominationToken.address, { from }) + const receipt = await payroll.addAllowedToken(DAI.address, { from }) const event = getEvent(receipt, 'AddAllowedToken') - assert.equal(event.token, denominationToken.address, 'denomination token address should match') + assert.equal(event.token, DAI.address, 'denomination token address should match') assert.equal(await payroll.getAllowedTokensArrayLength(), 1, 'allowed tokens length does not match') - assert(await payroll.isTokenAllowed(denominationToken.address), 'denomination token should be allowed') + assert(await payroll.isTokenAllowed(DAI.address), 'denomination token should be allowed') }) it('can allow a the zero address', async () => { @@ -57,15 +51,15 @@ contract('Payroll allowed tokens,', ([owner, employee, anyone]) => { }) it('can allow multiple tokens', async () => { - const erc20Token1 = await deployErc20TokenAndDeposit(owner, finance, 'Token 1', 18) - const erc20Token2 = await deployErc20TokenAndDeposit(owner, finance, 'Token 2', 16) + const erc20Token1 = await deployTokenAndDeposit(owner, finance, 'Token 1', 18) + const erc20Token2 = await deployTokenAndDeposit(owner, finance, 'Token 2', 16) - await payroll.addAllowedToken(denominationToken.address, { from }) + await payroll.addAllowedToken(DAI.address, { from }) await payroll.addAllowedToken(erc20Token1.address, { from }) await payroll.addAllowedToken(erc20Token2.address, { from }) assert.equal(await payroll.getAllowedTokensArrayLength(), 3, 'allowed tokens length does not match') - assert(await payroll.isTokenAllowed(denominationToken.address), 'denomination token should be allowed') + assert(await payroll.isTokenAllowed(DAI.address), 'denomination token should be allowed') assert(await payroll.isTokenAllowed(erc20Token1.address), 'ERC20 token 1 should be allowed') assert(await payroll.isTokenAllowed(erc20Token2.address), 'ERC20 token 2 should be allowed') }) @@ -77,7 +71,7 @@ contract('Payroll allowed tokens,', ([owner, employee, anyone]) => { before('deploy multiple tokens and set rates', async () => { MAX_ALLOWED_TOKENS = (await payrollBase.getMaxAllowedTokens()).valueOf() for (let i = 0; i < MAX_ALLOWED_TOKENS; i++) { - const token = await deployErc20TokenAndDeposit(owner, finance, `Token ${i}`, 18); + const token = await deployTokenAndDeposit(owner, finance, `Token ${i}`, 18); tokenAddresses.push(token.address) } }) @@ -87,13 +81,13 @@ contract('Payroll allowed tokens,', ([owner, employee, anyone]) => { assert.equal(await payroll.getAllowedTokensArrayLength(), MAX_ALLOWED_TOKENS, 'amount of allowed tokens does not match') const rates = tokenAddresses.map(() => 5) - await setTokenRates(priceFeed, denominationToken, tokenAddresses, rates) + await setTokenRates(priceFeed, USD, tokenAddresses, rates) await payroll.addEmployee(employee, 100000, 'Boss', NOW - ONE_MONTH, { from: owner }) }) it('can not add one more token', async () => { - const erc20Token = await deployErc20TokenAndDeposit(owner, finance, 'Extra token', 18) + const erc20Token = await deployTokenAndDeposit(owner, finance, 'Extra token', 18) await assertRevert(payroll.addAllowedToken(erc20Token.address), 'PAYROLL_MAX_ALLOWED_TOKENS') }) @@ -114,38 +108,38 @@ contract('Payroll allowed tokens,', ([owner, employee, anyone]) => { const from = anyone it('reverts', async () => { - await assertRevert(payroll.addAllowedToken(denominationToken.address, { from }), 'APP_AUTH_FAILED') + await assertRevert(payroll.addAllowedToken(DAI.address, { from }), 'APP_AUTH_FAILED') }) }) }) context('when it has not been initialized yet', function () { it('reverts', async () => { - await assertRevert(payroll.addAllowedToken(denominationToken.address, { from: owner }), 'APP_AUTH_FAILED') + await assertRevert(payroll.addAllowedToken(DAI.address, { from: owner }), 'APP_AUTH_FAILED') }) }) }) describe('isTokenAllowed', () => { context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the given token is not the zero address', () => { context('when the requested token was allowed', () => { beforeEach('allow denomination token', async () => { - await payroll.addAllowedToken(denominationToken.address, { from: owner }) + await payroll.addAllowedToken(DAI.address, { from: owner }) }) it('returns true', async () => { - assert(await payroll.isTokenAllowed(denominationToken.address), 'token should be allowed') + assert(await payroll.isTokenAllowed(DAI.address), 'token should be allowed') }) }) context('when the requested token was not allowed yet', () => { it('returns false', async () => { - assert.isFalse(await payroll.isTokenAllowed(denominationToken.address), 'token should not be allowed') + assert.isFalse(await payroll.isTokenAllowed(DAI.address), 'token should not be allowed') }) }) }) @@ -159,7 +153,7 @@ contract('Payroll allowed tokens,', ([owner, employee, anyone]) => { context('when it has not been initialized yet', function () { it('reverts', async () => { - await assertRevert(payroll.isTokenAllowed(denominationToken.address), 'INIT_NOT_INITIALIZED') + await assertRevert(payroll.isTokenAllowed(DAI.address), 'INIT_NOT_INITIALIZED') }) }) }) diff --git a/future-apps/payroll/test/contracts/Payroll_bonuses.test.js b/future-apps/payroll/test/contracts/Payroll_bonuses.test.js index c2a42728a8..ba1710f322 100644 --- a/future-apps/payroll/test/contracts/Payroll_bonuses.test.js +++ b/future-apps/payroll/test/contracts/Payroll_bonuses.test.js @@ -1,19 +1,13 @@ const PAYMENT_TYPES = require('../helpers/payment_types') -const setTokenRates = require('../helpers/set_token_rates')(web3) +const { MAX_UINT256 } = require('../helpers/numbers')(web3) const { assertRevert } = require('@aragon/test-helpers/assertThrow') -const { bn, maxUint256 } = require('../helpers/numbers')(web3) const { getEvents, getEventArgument } = require('../helpers/events') -const { deployErc20TokenAndDeposit, deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { NOW, ONE_MONTH, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) +const { USD, deployDAI, deployANT, DAI_RATE, ANT_RATE, setTokenRates } = require('../helpers/tokens.js')(artifacts, web3) contract('Payroll bonuses', ([owner, employee, anyone]) => { - let dao, payroll, payrollBase, finance, vault, priceFeed, denominationToken, anotherToken, anotherTokenRate - - const NOW = 1553703809 // random fixed timestamp in seconds - const ONE_MONTH = 60 * 60 * 24 * 31 - const TWO_MONTHS = ONE_MONTH * 2 - const RATE_EXPIRATION_TIME = TWO_MONTHS - - const TOKEN_DECIMALS = 18 + let dao, payroll, payrollBase, finance, vault, priceFeed, DAI, ANT const increaseTime = async seconds => { await payroll.mockIncreaseTime(seconds) @@ -22,8 +16,8 @@ contract('Payroll bonuses', ([owner, employee, anyone]) => { before('deploy base apps and tokens', async () => { ({ dao, finance, vault, payrollBase } = await deployContracts(owner)) - anotherToken = await deployErc20TokenAndDeposit(owner, finance, 'Another token', TOKEN_DECIMALS) - denominationToken = await deployErc20TokenAndDeposit(owner, finance, 'Denomination Token', TOKEN_DECIMALS) + ANT = await deployANT(owner, finance) + DAI = await deployDAI(owner, finance) }) beforeEach('create payroll and price feed instance', async () => { @@ -32,8 +26,8 @@ contract('Payroll bonuses', ([owner, employee, anyone]) => { describe('addBonus', () => { context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the sender has permissions', () => { @@ -91,7 +85,7 @@ contract('Payroll bonuses', ([owner, employee, anyone]) => { }) context('when the given bonus way greater than zero', () => { - const amount = maxUint256() + const amount = MAX_UINT256 it('reverts', async () => { await payroll.addBonus(employeeId, 1, { from }) @@ -145,13 +139,12 @@ contract('Payroll bonuses', ([owner, employee, anyone]) => { describe('bonus payday', () => { context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) beforeEach('set token rates', async () => { - anotherTokenRate = bn(5) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate]) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE]) }) context('when the sender is an employee', () => { @@ -166,13 +159,13 @@ contract('Payroll bonuses', ([owner, employee, anyone]) => { }) context('when the employee has already set some token allocations', () => { - const denominationTokenAllocation = 80 - const anotherTokenAllocation = 20 + const allocationDAI = 80 + const allocationANT = 20 beforeEach('set tokens allocation', async () => { - await payroll.addAllowedToken(anotherToken.address, { from: owner }) - await payroll.addAllowedToken(denominationToken.address, { from: owner }) - await payroll.determineAllocation([denominationToken.address, anotherToken.address], [denominationTokenAllocation, anotherTokenAllocation], { from }) + await payroll.addAllowedToken(ANT.address, { from: owner }) + await payroll.addAllowedToken(DAI.address, { from: owner }) + await payroll.determineAllocation([DAI.address, ANT.address], [allocationDAI, allocationANT], { from }) }) context('when the employee has a pending bonus', () => { @@ -184,22 +177,22 @@ contract('Payroll bonuses', ([owner, employee, anyone]) => { }) const assertTransferredAmounts = (requestedAmount, expectedRequestedAmount = requestedAmount) => { - const requestedDenominationTokenAmount = parseInt(expectedRequestedAmount * denominationTokenAllocation / 100) - const requestedAnotherTokenAmount = expectedRequestedAmount * anotherTokenAllocation / 100 + const requestedDAI = DAI_RATE.mul(expectedRequestedAmount * allocationDAI / 100).trunc() + const requestedANT = ANT_RATE.mul(expectedRequestedAmount * allocationANT / 100).trunc() it('transfers all the pending bonus', async () => { - const previousDenominationTokenBalance = await denominationToken.balanceOf(employee) - const previousAnotherTokenBalance = await anotherToken.balanceOf(employee) + const previousDAI = await DAI.balanceOf(employee) + const previousANT = await ANT.balanceOf(employee) await payroll.payday(PAYMENT_TYPES.BONUS, requestedAmount, { from }) - const currentDenominationTokenBalance = await denominationToken.balanceOf(employee) - const expectedDenominationTokenBalance = previousDenominationTokenBalance.plus(requestedDenominationTokenAmount); - assert.equal(currentDenominationTokenBalance.toString(), expectedDenominationTokenBalance.toString(), 'current denomination token balance does not match') + const currentDAI = await DAI.balanceOf(employee) + const expectedDAI = previousDAI.plus(requestedDAI) + assert.equal(currentDAI.toString(), expectedDAI.toString(), 'current DAI balance does not match') - const currentAnotherTokenBalance = await anotherToken.balanceOf(employee) - const expectedAnotherTokenBalance = anotherTokenRate.mul(requestedAnotherTokenAmount).plus(previousAnotherTokenBalance).trunc() - assert.equal(currentAnotherTokenBalance.toString(), expectedAnotherTokenBalance.toString(), 'current token balance does not match') + const currentANT = await ANT.balanceOf(employee) + const expectedANT = previousANT.plus(requestedANT) + assert.equal(currentANT.toString(), expectedANT.toString(), 'current ANT balance does not match') }) it('emits one event per allocated token', async () => { @@ -208,17 +201,17 @@ contract('Payroll bonuses', ([owner, employee, anyone]) => { const events = receipt.logs.filter(l => l.event === 'SendPayment') assert.equal(events.length, 2, 'should have emitted two events') - const denominationTokenEvent = events.find(e => e.args.token === denominationToken.address).args - assert.equal(denominationTokenEvent.employee, employee, 'employee address does not match') - assert.equal(denominationTokenEvent.token, denominationToken.address, 'denomination token address does not match') - assert.equal(denominationTokenEvent.amount.toString(), requestedDenominationTokenAmount, 'payment amount does not match') - assert.equal(denominationTokenEvent.paymentReference, 'Bonus', 'payment reference does not match') - - const anotherTokenEvent = events.find(e => e.args.token === anotherToken.address).args - assert.equal(anotherTokenEvent.employee, employee, 'employee address does not match') - assert.equal(anotherTokenEvent.token, anotherToken.address, 'token address does not match') - assert.equal(anotherTokenEvent.amount.div(anotherTokenRate).trunc().toString(), parseInt(requestedAnotherTokenAmount), 'payment amount does not match') - assert.equal(anotherTokenEvent.paymentReference, 'Bonus', 'payment reference does not match') + const eventDAI = events.find(e => e.args.token === DAI.address).args + assert.equal(eventDAI.employee, employee, 'employee address does not match') + assert.equal(eventDAI.token, DAI.address, 'DAI address does not match') + assert.equal(eventDAI.amount.toString(), requestedDAI, 'payment amount does not match') + assert.equal(eventDAI.paymentReference, 'Bonus', 'payment reference does not match') + + const eventANT = events.find(e => e.args.token === ANT.address).args + assert.equal(eventANT.employee, employee, 'employee address does not match') + assert.equal(eventANT.token, ANT.address, 'token address does not match') + assert.equal(eventANT.amount.toString(), requestedANT, 'payment amount does not match') + assert.equal(eventANT.paymentReference, 'Bonus', 'payment reference does not match') }) } @@ -244,7 +237,7 @@ contract('Payroll bonuses', ([owner, employee, anyone]) => { context('when exchange rates are expired', () => { beforeEach('expire exchange rates', async () => { const expiredTimestamp = (await payroll.getTimestampPublic()).sub(RATE_EXPIRATION_TIME + 1) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate], expiredTimestamp) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE], expiredTimestamp) }) it('reverts', async () => { @@ -297,7 +290,7 @@ contract('Payroll bonuses', ([owner, employee, anyone]) => { context('when exchange rates are expired', () => { beforeEach('expire exchange rates', async () => { const expiredTimestamp = (await payroll.getTimestampPublic()).sub(RATE_EXPIRATION_TIME + 1) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate], expiredTimestamp) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE], expiredTimestamp) }) it('reverts', async () => { @@ -388,7 +381,7 @@ contract('Payroll bonuses', ([owner, employee, anyone]) => { context('when exchange rates are expired', () => { beforeEach('expire exchange rates', async () => { const expiredTimestamp = (await payroll.getTimestampPublic()).sub(RATE_EXPIRATION_TIME + 1) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate], expiredTimestamp) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE], expiredTimestamp) }) it('reverts', async () => { diff --git a/future-apps/payroll/test/contracts/Payroll_forwarding.test.js b/future-apps/payroll/test/contracts/Payroll_forwarding.test.js index 8f185ad8f9..e83ac0e8f0 100644 --- a/future-apps/payroll/test/contracts/Payroll_forwarding.test.js +++ b/future-apps/payroll/test/contracts/Payroll_forwarding.test.js @@ -1,23 +1,18 @@ const { assertRevert } = require('@aragon/test-helpers/assertThrow') -const { encodeCallScript } = require('@aragon/test-helpers/evmScript') const { getEventArgument } = require('../helpers/events') -const { deployErc20TokenAndDeposit, deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { encodeCallScript } = require('@aragon/test-helpers/evmScript') +const { USD, deployDAI } = require('../helpers/tokens.js')(artifacts, web3) +const { NOW, ONE_MONTH, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) const ExecutionTarget = artifacts.require('ExecutionTarget') contract('Payroll forwarding,', ([owner, employee, anyone]) => { - let dao, payroll, payrollBase, finance, vault, priceFeed, denominationToken - - const NOW = 1553703809 // random fixed timestamp in seconds - const ONE_MONTH = 60 * 60 * 24 * 31 - const TWO_MONTHS = ONE_MONTH * 2 - const RATE_EXPIRATION_TIME = TWO_MONTHS - - const TOKEN_DECIMALS = 18 + let dao, payroll, payrollBase, finance, vault, priceFeed, DAI before('deploy base apps and tokens', async () => { ({ dao, finance, vault, payrollBase } = await deployContracts(owner)) - denominationToken = await deployErc20TokenAndDeposit(owner, finance, 'Denomination Token', TOKEN_DECIMALS) + DAI = await deployDAI(owner, finance) }) beforeEach('create payroll and price feed instance', async () => { @@ -26,8 +21,8 @@ contract('Payroll forwarding,', ([owner, employee, anyone]) => { describe('isForwarder', () => { context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) it('returns true', async () => { @@ -44,8 +39,8 @@ contract('Payroll forwarding,', ([owner, employee, anyone]) => { describe('canForward', () => { context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the sender is an employee', () => { @@ -101,8 +96,8 @@ contract('Payroll forwarding,', ([owner, employee, anyone]) => { }) context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the sender is an employee', () => { diff --git a/future-apps/payroll/test/contracts/Payroll_gas_costs.test.js b/future-apps/payroll/test/contracts/Payroll_gas_costs.test.js index f1c64be636..8b9035282c 100644 --- a/future-apps/payroll/test/contracts/Payroll_gas_costs.test.js +++ b/future-apps/payroll/test/contracts/Payroll_gas_costs.test.js @@ -1,21 +1,16 @@ const PAYMENT_TYPES = require('../helpers/payment_types') -const setTokenRates = require('../helpers/set_token_rates')(web3) const { annualSalaryPerSecond } = require('../helpers/numbers')(web3) -const { deployErc20TokenAndDeposit, deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { NOW, ONE_MONTH, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) +const { USD, deployDAI, deployANT, DAI_RATE, ANT_RATE, setTokenRates } = require('../helpers/tokens.js')(artifacts, web3) contract('Payroll gas costs', ([owner, employee, anotherEmployee]) => { - let dao, payroll, payrollBase, finance, vault, priceFeed, denominationToken - - const NOW = 1553703809 // random fixed timestamp in seconds - const ONE_MONTH = 60 * 60 * 24 * 31 - const TWO_MONTHS = ONE_MONTH * 2 - const RATE_EXPIRATION_TIME = TWO_MONTHS - - const TOKEN_DECIMALS = 18 + let dao, payroll, payrollBase, finance, vault, priceFeed, DAI, ANT before('deploy base apps and tokens', async () => { ({ dao, finance, vault, payrollBase } = await deployContracts(owner)) - denominationToken = await deployErc20TokenAndDeposit(owner, finance, 'Denomination Token', TOKEN_DECIMALS) + DAI = await deployDAI(owner, finance) + ANT = await deployANT(owner, finance) }) beforeEach('create payroll and price feed instance', async () => { @@ -23,52 +18,42 @@ contract('Payroll gas costs', ([owner, employee, anotherEmployee]) => { }) describe('gas costs', () => { - let erc20Token1, erc20Token2 - - before('deploy more tokens', async () => { - erc20Token1 = await deployErc20TokenAndDeposit(owner, finance, 'Token 1', 16) - erc20Token2 = await deployErc20TokenAndDeposit(owner, finance, 'Token 2', 18) - }) - - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) const startDate = NOW - ONE_MONTH - const salary = annualSalaryPerSecond(100000, TOKEN_DECIMALS) + const salary = annualSalaryPerSecond(100000) await payroll.addEmployee(employee, salary, 'Boss', startDate) await payroll.addEmployee(anotherEmployee, salary, 'Manager', startDate) }) - context('when there are not allowed tokens yet', function () { - it('expends ~330k gas for a single allowed token', async () => { - await payroll.addAllowedToken(denominationToken.address) - await payroll.determineAllocation([denominationToken.address], [100], { from: employee }) + beforeEach('allow tokens and set rates', async () => { + await payroll.addAllowedToken(DAI.address, { from: owner }) + await payroll.addAllowedToken(ANT.address, { from: owner }) - const { receipt: { cumulativeGasUsed } } = await payroll.payday(PAYMENT_TYPES.PAYROLL, 0, { from: employee }) - - assert.isBelow(cumulativeGasUsed, 330000, 'payout gas cost for a single allowed token should be ~314k') - }) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE]) }) - context('when there are some allowed tokens', function () { - const erc20Token1Rate = 2, erc20Token2Rate = 5 + context('when there is only one allowed token', function () { + it('expends ~335k gas for a single allowed token', async () => { + await payroll.determineAllocation([DAI.address], [100], { from: employee }) - beforeEach('allow tokens and set rates', async () => { - await payroll.addAllowedToken(denominationToken.address, { from: owner }) - await payroll.addAllowedToken(erc20Token1.address, { from: owner }) - await payroll.addAllowedToken(erc20Token2.address, { from: owner }) - await setTokenRates(priceFeed, denominationToken, [erc20Token1, erc20Token2], [erc20Token1Rate, erc20Token2Rate]) + const { receipt: { cumulativeGasUsed } } = await payroll.payday(PAYMENT_TYPES.PAYROLL, 0, { from: employee }) + + assert.isBelow(cumulativeGasUsed, 335000, 'payout gas cost for a single allowed token should be ~335k') }) + }) - it('expends ~270k gas per allowed token', async () => { - await payroll.determineAllocation([denominationToken.address, erc20Token1.address], [60, 40], { from: employee }) + context('when there are two allowed token', function () { + it('expends ~295k gas per allowed token', async () => { + await payroll.determineAllocation([DAI.address], [100], { from: employee }) const { receipt: { cumulativeGasUsed: employeePayoutGasUsed } } = await payroll.payday(PAYMENT_TYPES.PAYROLL, 0, { from: employee }) - await payroll.determineAllocation([denominationToken.address, erc20Token1.address, erc20Token2.address], [65, 25, 10], { from: anotherEmployee }) + await payroll.determineAllocation([DAI.address, ANT.address], [60, 40], { from: anotherEmployee }) const { receipt: { cumulativeGasUsed: anotherEmployeePayoutGasUsed } } = await payroll.payday(PAYMENT_TYPES.PAYROLL, 0, { from: anotherEmployee }) const gasPerAllowedToken = anotherEmployeePayoutGasUsed - employeePayoutGasUsed - assert.isBelow(gasPerAllowedToken, 280000, 'payout gas cost increment per allowed token should be ~270k') + assert.isBelow(gasPerAllowedToken, 295000, 'payout gas cost increment per allowed token should be ~295k') }) }) }) diff --git a/future-apps/payroll/test/contracts/Payroll_get_employee.test.js b/future-apps/payroll/test/contracts/Payroll_get_employee.test.js index c344c0c287..03c7ffaeb1 100644 --- a/future-apps/payroll/test/contracts/Payroll_get_employee.test.js +++ b/future-apps/payroll/test/contracts/Payroll_get_employee.test.js @@ -1,23 +1,18 @@ -const { maxUint64 } = require('../helpers/numbers')(web3) +const { MAX_UINT64 } = require('../helpers/numbers')(web3) const { assertRevert } = require('@aragon/test-helpers/assertThrow') const { getEventArgument } = require('../helpers/events') -const { deployErc20TokenAndDeposit, deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { USD, deployDAI } = require('../helpers/tokens.js')(artifacts, web3) +const { NOW, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) contract('Payroll employee getters', ([owner, employee]) => { - let dao, payroll, payrollBase, finance, vault, priceFeed, denominationToken - - const NOW = 1553703809 // random fixed timestamp in seconds - const ONE_MONTH = 60 * 60 * 24 * 31 - const TWO_MONTHS = ONE_MONTH * 2 - const RATE_EXPIRATION_TIME = TWO_MONTHS - - const TOKEN_DECIMALS = 18 + let dao, payroll, payrollBase, finance, vault, priceFeed, DAI const currentTimestamp = async () => payroll.getTimestampPublic() before('deploy base apps and tokens', async () => { ({ dao, finance, vault, payrollBase } = await deployContracts(owner)) - denominationToken = await deployErc20TokenAndDeposit(owner, finance, 'Denomination Token', TOKEN_DECIMALS) + DAI = await deployDAI(owner, finance) }) beforeEach('create payroll and price feed instance', async () => { @@ -26,8 +21,8 @@ contract('Payroll employee getters', ([owner, employee]) => { describe('getEmployee', () => { context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the given id exists', () => { @@ -47,7 +42,7 @@ contract('Payroll employee getters', ([owner, employee]) => { assert.equal(accruedSalary, 0, 'employee accrued salary does not match') assert.equal(salary.toString(), 1000, 'employee salary does not match') assert.equal(lastPayroll.toString(), (await currentTimestamp()).toString(), 'employee last payroll does not match') - assert.equal(endDate.toString(), maxUint64(), 'employee end date does not match') + assert.equal(endDate.toString(), MAX_UINT64, 'employee end date does not match') }) }) @@ -71,8 +66,8 @@ contract('Payroll employee getters', ([owner, employee]) => { describe('getEmployeeByAddress', () => { context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the given address exists', () => { @@ -93,7 +88,7 @@ contract('Payroll employee getters', ([owner, employee]) => { assert.equal(reimbursements.toString(), 0, 'employee reimbursements does not match') assert.equal(accruedSalary.toString(), 0, 'employee accrued salary does not match') assert.equal(lastPayroll.toString(), (await currentTimestamp()).toString(), 'employee last payroll does not match') - assert.equal(endDate.toString(), maxUint64(), 'employee end date does not match') + assert.equal(endDate.toString(), MAX_UINT64, 'employee end date does not match') }) }) diff --git a/future-apps/payroll/test/contracts/Payroll_initialize.test.js b/future-apps/payroll/test/contracts/Payroll_initialize.test.js index 95ab36b032..a9c9c5a5dc 100644 --- a/future-apps/payroll/test/contracts/Payroll_initialize.test.js +++ b/future-apps/payroll/test/contracts/Payroll_initialize.test.js @@ -1,21 +1,15 @@ +const { USD } = require('../helpers/tokens.js')(artifacts, web3) const { assertRevert } = require('@aragon/test-helpers/assertThrow') -const { deployErc20TokenAndDeposit, deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { NOW, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' contract('Payroll initialization', ([owner]) => { - let dao, payroll, payrollBase, finance, vault, priceFeed, denominationToken - - const NOW = 1553703809 // random fixed timestamp in seconds - const ONE_MONTH = 60 * 60 * 24 * 31 - const TWO_MONTHS = ONE_MONTH * 2 - const RATE_EXPIRATION_TIME = TWO_MONTHS - - const TOKEN_DECIMALS = 18 + let dao, payroll, payrollBase, finance, vault, priceFeed before('deploy base apps and tokens', async () => { ({ dao, finance, vault, payrollBase } = await deployContracts(owner)) - denominationToken = await deployErc20TokenAndDeposit(owner, finance, 'Denomination Token', TOKEN_DECIMALS) }) beforeEach('create payroll and price feed instance', async () => { @@ -27,7 +21,7 @@ contract('Payroll initialization', ([owner]) => { it('cannot initialize the base app', async () => { assert(await payrollBase.isPetrified(), 'base payroll app should be petrified') - await assertRevert(payrollBase.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from }), 'INIT_ALREADY_INITIALIZED') + await assertRevert(payrollBase.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from }), 'INIT_ALREADY_INITIALIZED') }) context('when it has not been initialized yet', function () { @@ -38,32 +32,32 @@ contract('Payroll initialization', ([owner]) => { it('reverts when passing an expiration time lower than or equal to a minute', async () => { const ONE_MINUTE = 60 - await assertRevert(payroll.initialize(finance.address, denominationToken.address, priceFeed.address, ONE_MINUTE, { from }), 'PAYROLL_EXPIRY_TIME_TOO_SHORT') + await assertRevert(payroll.initialize(finance.address, USD, priceFeed.address, ONE_MINUTE, { from }), 'PAYROLL_EXPIRY_TIME_TOO_SHORT') }) it('reverts when passing an invalid finance instance', async () => { - await assertRevert(payroll.initialize(ZERO_ADDRESS, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from }), 'PAYROLL_FINANCE_NOT_CONTRACT') + await assertRevert(payroll.initialize(ZERO_ADDRESS, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from }), 'PAYROLL_FINANCE_NOT_CONTRACT') }) it('reverts when passing an invalid feed instance', async () => { - await assertRevert(payroll.initialize(finance.address, denominationToken.address, ZERO_ADDRESS, RATE_EXPIRATION_TIME, { from }), 'PAYROLL_FEED_NOT_CONTRACT') + await assertRevert(payroll.initialize(finance.address, USD, ZERO_ADDRESS, RATE_EXPIRATION_TIME, { from }), 'PAYROLL_FEED_NOT_CONTRACT') }) }) context('when it has already been initialized', function () { beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from }) + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from }) }) it('cannot be initialized again', async () => { - await assertRevert(payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from }), 'INIT_ALREADY_INITIALIZED') + await assertRevert(payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from }), 'INIT_ALREADY_INITIALIZED') }) it('has a price feed instance, a finance instance, a denomination token and a rate expiration time', async () => { assert.equal(await payroll.feed(), priceFeed.address, 'feed address does not match') assert.equal(await payroll.finance(), finance.address, 'finance address should match') - assert.equal(await payroll.denominationToken(), denominationToken.address, 'denomination token does not match') assert.equal(await payroll.rateExpiryTime(), RATE_EXPIRATION_TIME, 'rate expiration time does not match') + assert.equal(web3.toChecksumAddress(await payroll.denominationToken()), USD, 'denomination token address does not match') }) }) }) diff --git a/future-apps/payroll/test/contracts/Payroll_modify_employee.test.js b/future-apps/payroll/test/contracts/Payroll_modify_employee.test.js index 37dceca2ba..9aca9f0a6f 100644 --- a/future-apps/payroll/test/contracts/Payroll_modify_employee.test.js +++ b/future-apps/payroll/test/contracts/Payroll_modify_employee.test.js @@ -1,19 +1,14 @@ +const { USD } = require('../helpers/tokens.js')(artifacts, web3) const { assertRevert } = require('@aragon/test-helpers/assertThrow') const { getEvents, getEventArgument } = require('../helpers/events') -const { maxUint256, annualSalaryPerSecond } = require('../helpers/numbers')(web3) -const { deployErc20TokenAndDeposit, deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { NOW, ONE_MONTH, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { MAX_UINT256, annualSalaryPerSecond } = require('../helpers/numbers')(web3) +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' contract('Payroll employees modification', ([owner, employee, anotherEmployee, anyone]) => { - let dao, payroll, payrollBase, finance, vault, priceFeed, denominationToken - - const NOW = 1553703809 // random fixed timestamp in seconds - const ONE_MONTH = 60 * 60 * 24 * 31 - const TWO_MONTHS = ONE_MONTH * 2 - const RATE_EXPIRATION_TIME = TWO_MONTHS - - const TOKEN_DECIMALS = 18 + let dao, payroll, payrollBase, finance, vault, priceFeed const increaseTime = async seconds => { await payroll.mockIncreaseTime(seconds) @@ -22,7 +17,6 @@ contract('Payroll employees modification', ([owner, employee, anotherEmployee, a before('deploy base apps and tokens', async () => { ({ dao, finance, vault, payrollBase } = await deployContracts(owner)) - denominationToken = await deployErc20TokenAndDeposit(owner, finance, 'Denomination Token', TOKEN_DECIMALS) }) beforeEach('create payroll and price feed instance', async () => { @@ -31,8 +25,8 @@ contract('Payroll employees modification', ([owner, employee, anotherEmployee, a describe('setEmployeeSalary', () => { context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the sender has permissions', () => { @@ -40,7 +34,7 @@ contract('Payroll employees modification', ([owner, employee, anotherEmployee, a context('when the given employee exists', () => { let employeeId - const previousSalary = annualSalaryPerSecond(100000, TOKEN_DECIMALS) + const previousSalary = annualSalaryPerSecond(100000) beforeEach('add employee', async () => { const receipt = await payroll.addEmployee(employee, previousSalary, 'Boss', await payroll.getTimestampPublic()) @@ -103,7 +97,7 @@ contract('Payroll employees modification', ([owner, employee, anotherEmployee, a context('when the employee is owed a huge salary amount', () => { beforeEach('accrued a huge salary amount', async () => { - await payroll.setEmployeeSalary(employeeId, maxUint256(), { from }) + await payroll.setEmployeeSalary(employeeId, MAX_UINT256, { from }) await increaseTime(ONE_MONTH) }) @@ -162,8 +156,8 @@ contract('Payroll employees modification', ([owner, employee, anotherEmployee, a describe('changeAddressByEmployee', () => { context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the sender is an employee', () => { diff --git a/future-apps/payroll/test/contracts/Payroll_payday.test.js b/future-apps/payroll/test/contracts/Payroll_payday.test.js index 0b0b58f7b6..ad683893b3 100644 --- a/future-apps/payroll/test/contracts/Payroll_payday.test.js +++ b/future-apps/payroll/test/contracts/Payroll_payday.test.js @@ -1,19 +1,13 @@ const PAYMENT_TYPES = require('../helpers/payment_types') -const setTokenRates = require('../helpers/set_token_rates')(web3) const { assertRevert } = require('@aragon/test-helpers/assertThrow') -const { bn, maxUint256 } = require('../helpers/numbers')(web3) +const { MAX_UINT256 } = require('../helpers/numbers')(web3) const { getEventArgument } = require('../helpers/events') -const { deployErc20TokenAndDeposit, deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { NOW, ONE_MONTH, TWO_MONTHS, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) +const { USD, deployDAI, deployANT, DAI_RATE, ANT_RATE, setTokenRates } = require('../helpers/tokens.js')(artifacts, web3) contract('Payroll payday', ([owner, employee, anyone]) => { - let dao, payroll, payrollBase, finance, vault, priceFeed, denominationToken, anotherToken, anotherTokenRate - - const NOW = 1553703809 // random fixed timestamp in seconds - const ONE_MONTH = 60 * 60 * 24 * 31 - const TWO_MONTHS = ONE_MONTH * 2 - const RATE_EXPIRATION_TIME = TWO_MONTHS - - const TOKEN_DECIMALS = 18 + let dao, payroll, payrollBase, finance, vault, priceFeed, DAI, ANT const increaseTime = async seconds => { await payroll.mockIncreaseTime(seconds) @@ -22,8 +16,8 @@ contract('Payroll payday', ([owner, employee, anyone]) => { before('deploy base apps and tokens', async () => { ({ dao, finance, vault, payrollBase } = await deployContracts(owner)) - anotherToken = await deployErc20TokenAndDeposit(owner, finance, 'Another token', TOKEN_DECIMALS) - denominationToken = await deployErc20TokenAndDeposit(owner, finance, 'Denomination Token', TOKEN_DECIMALS) + ANT = await deployANT(owner, finance) + DAI = await deployDAI(owner, finance) }) beforeEach('create payroll and price feed instance', async () => { @@ -32,13 +26,12 @@ contract('Payroll payday', ([owner, employee, anyone]) => { describe('payroll payday', () => { context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) beforeEach('set token rates', async () => { - anotherTokenRate = bn(5) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate]) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE]) }) context('when the sender is an employee', () => { @@ -54,32 +47,32 @@ contract('Payroll payday', ([owner, employee, anyone]) => { }) context('when the employee has already set some token allocations', () => { - const denominationTokenAllocation = 80 - const anotherTokenAllocation = 20 + const allocationDAI = 80 + const allocationANT = 20 beforeEach('set tokens allocation', async () => { - await payroll.addAllowedToken(anotherToken.address, { from: owner }) - await payroll.addAllowedToken(denominationToken.address, { from: owner }) - await payroll.determineAllocation([denominationToken.address, anotherToken.address], [denominationTokenAllocation, anotherTokenAllocation], { from }) + await payroll.addAllowedToken(ANT.address, { from: owner }) + await payroll.addAllowedToken(DAI.address, { from: owner }) + await payroll.determineAllocation([DAI.address, ANT.address], [allocationDAI, allocationANT], { from }) }) const assertTransferredAmounts = (requestedAmount, expectedRequestedAmount = requestedAmount) => { - const requestedDenominationTokenAmount = Math.round(expectedRequestedAmount * denominationTokenAllocation / 100) - const requestedAnotherTokenAmount = Math.round(expectedRequestedAmount * anotherTokenAllocation / 100) + const requestedDAI = DAI_RATE.mul(expectedRequestedAmount * allocationDAI / 100).trunc() + const requestedANT = ANT_RATE.mul(expectedRequestedAmount * allocationANT / 100).trunc() it('transfers the requested salary amount', async () => { - const previousDenominationTokenBalance = await denominationToken.balanceOf(employee) - const previousAnotherTokenBalance = await anotherToken.balanceOf(employee) + const previousDAI = await DAI.balanceOf(employee) + const previousANT = await ANT.balanceOf(employee) await payroll.payday(PAYMENT_TYPES.PAYROLL, requestedAmount, { from }) - const currentDenominationTokenBalance = await denominationToken.balanceOf(employee) - const expectedDenominationTokenBalance = previousDenominationTokenBalance.plus(requestedDenominationTokenAmount); - assert.equal(currentDenominationTokenBalance.toString(), expectedDenominationTokenBalance.toString(), 'current denomination token balance does not match') + const currentDAI = await DAI.balanceOf(employee) + const expectedDAI = previousDAI.plus(requestedDAI); + assert.equal(currentDAI.toString(), expectedDAI.toString(), 'current DAI balance does not match') - const currentAnotherTokenBalance = await anotherToken.balanceOf(employee) - const expectedAnotherTokenBalance = anotherTokenRate.mul(requestedAnotherTokenAmount).plus(previousAnotherTokenBalance).trunc() - assert.equal(currentAnotherTokenBalance.toString(), expectedAnotherTokenBalance.toString(), 'current token balance does not match') + const currentANT = await ANT.balanceOf(employee) + const expectedANT = previousANT.plus(requestedANT) + assert.equal(currentANT.toString(), expectedANT.toString(), 'current ANT balance does not match') }) it('emits one event per allocated token', async () => { @@ -88,43 +81,43 @@ contract('Payroll payday', ([owner, employee, anyone]) => { const events = receipt.logs.filter(l => l.event === 'SendPayment') assert.equal(events.length, 2, 'should have emitted two events') - const denominationTokenEvent = events.find(e => e.args.token === denominationToken.address).args - assert.equal(denominationTokenEvent.employee, employee, 'employee address does not match') - assert.equal(denominationTokenEvent.token, denominationToken.address, 'denomination token address does not match') - assert.equal(denominationTokenEvent.amount.toString(), requestedDenominationTokenAmount, 'payment amount does not match') - assert.equal(denominationTokenEvent.paymentReference, 'Payroll', 'payment reference does not match') + const eventDAI = events.find(e => e.args.token === DAI.address).args + assert.equal(eventDAI.employee, employee, 'employee address does not match') + assert.equal(eventDAI.token, DAI.address, 'DAI address does not match') + assert.equal(eventDAI.amount.toString(), requestedDAI, 'payment amount does not match') + assert.equal(eventDAI.paymentReference, 'Payroll', 'payment reference does not match') - const anotherTokenEvent = events.find(e => e.args.token === anotherToken.address).args - assert.equal(anotherTokenEvent.employee, employee, 'employee address does not match') - assert.equal(anotherTokenEvent.token, anotherToken.address, 'token address does not match') - assert.equal(anotherTokenEvent.amount.div(anotherTokenRate).trunc().toString(), requestedAnotherTokenAmount, 'payment amount does not match') - assert.equal(anotherTokenEvent.paymentReference, 'Payroll', 'payment reference does not match') + const eventANT = events.find(e => e.args.token === ANT.address).args + assert.equal(eventANT.employee, employee, 'employee address does not match') + assert.equal(eventANT.token, ANT.address, 'ANT address does not match') + assert.equal(eventANT.amount.toString(), requestedANT, 'payment amount does not match') + assert.equal(eventANT.paymentReference, 'Payroll', 'payment reference does not match') }) it('can be called multiple times between periods of time', async () => { // terminate employee in the future to ensure we can request payroll multiple times await payroll.terminateEmployee(employeeId, NOW + TWO_MONTHS + TWO_MONTHS, { from: owner }) - const previousDenominationTokenBalance = await denominationToken.balanceOf(employee) - const previousAnotherTokenBalance = await anotherToken.balanceOf(employee) + const previousDAI = await DAI.balanceOf(employee) + const previousANT = await ANT.balanceOf(employee) await payroll.payday(PAYMENT_TYPES.PAYROLL, requestedAmount, { from }) const newOwedAmount = salary * ONE_MONTH - const newDenominationTokenOwedAmount = Math.round(newOwedAmount * denominationTokenAllocation / 100) - const newAnotherTokenOwedAmount = Math.round(newOwedAmount * anotherTokenAllocation / 100) + const newDAIAmount = DAI_RATE.mul(newOwedAmount * allocationDAI / 100).trunc() + const newANTAmount = ANT_RATE.mul(newOwedAmount * allocationANT / 100).trunc() await increaseTime(ONE_MONTH) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate]) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE]) await payroll.payday(PAYMENT_TYPES.PAYROLL, newOwedAmount, { from }) - const currentDenominationTokenBalance = await denominationToken.balanceOf(employee) - const expectedDenominationTokenBalance = previousDenominationTokenBalance.plus(requestedDenominationTokenAmount + newDenominationTokenOwedAmount) - assert.equal(currentDenominationTokenBalance.toString(), expectedDenominationTokenBalance.toString(), 'current denomination token balance does not match') + const currentDAI = await DAI.balanceOf(employee) + const expectedDAI = previousDAI.plus(requestedDAI).plus(newDAIAmount) + assert.equal(currentDAI.toString(), expectedDAI.toString(), 'current DAI balance does not match') - const currentAnotherTokenBalance = await anotherToken.balanceOf(employee) - const expectedAnotherTokenBalance = anotherTokenRate.mul(requestedAnotherTokenAmount + newAnotherTokenOwedAmount).plus(previousAnotherTokenBalance) - assert.equal(currentAnotherTokenBalance.toString(), expectedAnotherTokenBalance.toString(), 'current token balance does not match') + const currentANT = await ANT.balanceOf(employee) + const expectedANT = previousANT.plus(requestedANT).plus(newANTAmount) + assert.equal(currentANT.toString(), expectedANT.toString(), 'current ANT balance does not match') }) } @@ -168,7 +161,7 @@ contract('Payroll payday', ([owner, employee, anyone]) => { context('when exchange rates are expired', () => { beforeEach('expire exchange rates', async () => { const expiredTimestamp = (await payroll.getTimestampPublic()).sub(RATE_EXPIRATION_TIME + 1) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate], expiredTimestamp) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE], expiredTimestamp) }) it('reverts', async () => { @@ -261,7 +254,7 @@ contract('Payroll payday', ([owner, employee, anyone]) => { context('when exchange rates are expired', () => { beforeEach('expire exchange rates', async () => { const expiredTimestamp = (await payroll.getTimestampPublic()).sub(RATE_EXPIRATION_TIME + 1) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate], expiredTimestamp) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE], expiredTimestamp) }) it('reverts', async () => { @@ -360,7 +353,7 @@ contract('Payroll payday', ([owner, employee, anyone]) => { beforeEach('accumulate some pending salary and renew token rates', async () => { await increaseTime(ONE_MONTH) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate]) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE]) }) context('when the requested amount is zero', () => { @@ -486,7 +479,7 @@ contract('Payroll payday', ([owner, employee, anyone]) => { context('when exchange rates are expired', () => { beforeEach('expire exchange rates', async () => { const expiredTimestamp = (await payroll.getTimestampPublic()).sub(RATE_EXPIRATION_TIME + 1) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate], expiredTimestamp) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE], expiredTimestamp) }) itReverts(requestedAmount, expiredRatesReason) @@ -570,13 +563,13 @@ contract('Payroll payday', ([owner, employee, anyone]) => { } context('when the employee has already set some token allocations', () => { - const denominationTokenAllocation = 80 - const anotherTokenAllocation = 20 + const allocationDAI = 80 + const allocationANT = 20 beforeEach('set tokens allocation', async () => { - await payroll.addAllowedToken(anotherToken.address, {from: owner}) - await payroll.addAllowedToken(denominationToken.address, {from: owner}) - await payroll.determineAllocation([denominationToken.address, anotherToken.address], [denominationTokenAllocation, anotherTokenAllocation], {from}) + await payroll.addAllowedToken(ANT.address, { from: owner }) + await payroll.addAllowedToken(DAI.address, { from: owner }) + await payroll.determineAllocation([DAI.address, ANT.address], [allocationDAI, allocationANT], { from }) }) itRevertsAnyAttemptToWithdrawPartialPayroll() @@ -588,7 +581,7 @@ contract('Payroll payday', ([owner, employee, anyone]) => { }) context('when the employee has a huge salary', () => { - const salary = maxUint256() + const salary = MAX_UINT256 beforeEach('add employee', async () => { const receipt = await payroll.addEmployee(employee, salary, 'Boss', await payroll.getTimestampPublic()) @@ -596,17 +589,17 @@ contract('Payroll payday', ([owner, employee, anyone]) => { }) context('when the employee has already set some token allocations', () => { - const denominationTokenAllocation = 80 - const anotherTokenAllocation = 20 + const allocationDAI = 80 + const allocationANT = 20 beforeEach('set tokens allocation', async () => { - await payroll.addAllowedToken(anotherToken.address, {from: owner}) - await payroll.addAllowedToken(denominationToken.address, {from: owner}) - await payroll.determineAllocation([denominationToken.address, anotherToken.address], [denominationTokenAllocation, anotherTokenAllocation], {from}) + await payroll.addAllowedToken(ANT.address, { from: owner }) + await payroll.addAllowedToken(DAI.address, { from: owner }) + await payroll.determineAllocation([DAI.address, ANT.address], [allocationDAI, allocationANT], { from }) }) context('when the employee has some pending salary', () => { - const owedSalary = maxUint256() + const owedSalary = MAX_UINT256 beforeEach('accumulate some pending salary', async () => { await increaseTime(ONE_MONTH) @@ -622,22 +615,22 @@ contract('Payroll payday', ([owner, employee, anyone]) => { const requestedAmount = 10000 const assertTransferredAmounts = requestedAmount => { - const requestedDenominationTokenAmount = Math.round(requestedAmount * denominationTokenAllocation / 100) - const requestedAnotherTokenAmount = Math.round(requestedAmount * anotherTokenAllocation / 100) + const requestedDAI = DAI_RATE.mul(requestedAmount * allocationDAI / 100).trunc() + const requestedANT = ANT_RATE.mul(requestedAmount * allocationANT / 100).trunc() it('transfers the requested salary amount', async () => { - const previousDenominationTokenBalance = await denominationToken.balanceOf(employee) - const previousAnotherTokenBalance = await anotherToken.balanceOf(employee) + const previousDAI = await DAI.balanceOf(employee) + const previousANT = await ANT.balanceOf(employee) await payroll.payday(PAYMENT_TYPES.PAYROLL, requestedAmount, { from }) - const currentDenominationTokenBalance = await denominationToken.balanceOf(employee) - const expectedDenominationTokenBalance = previousDenominationTokenBalance.plus(requestedDenominationTokenAmount); - assert.equal(currentDenominationTokenBalance.toString(), expectedDenominationTokenBalance.toString(), 'current denomination token balance does not match') + const currentDAI = await DAI.balanceOf(employee) + const expectedDAI = previousDAI.plus(requestedDAI) + assert.equal(currentDAI.toString(), expectedDAI.toString(), 'current DAI balance does not match') - const currentAnotherTokenBalance = await anotherToken.balanceOf(employee) - const expectedAnotherTokenBalance = anotherTokenRate.mul(requestedAnotherTokenAmount).plus(previousAnotherTokenBalance).trunc() - assert.equal(currentAnotherTokenBalance.toString(), expectedAnotherTokenBalance.toString(), 'current token balance does not match') + const currentANT = await ANT.balanceOf(employee) + const expectedANT = previousANT.plus(requestedANT) + assert.equal(currentANT.toString(), expectedANT.toString(), 'current ANT balance does not match') }) it('emits one event per allocated token', async () => { @@ -646,39 +639,39 @@ contract('Payroll payday', ([owner, employee, anyone]) => { const events = receipt.logs.filter(l => l.event === 'SendPayment') assert.equal(events.length, 2, 'should have emitted two events') - const denominationTokenEvent = events.find(e => e.args.token === denominationToken.address).args - assert.equal(denominationTokenEvent.employee, employee, 'employee address does not match') - assert.equal(denominationTokenEvent.token, denominationToken.address, 'denomination token address does not match') - assert.equal(denominationTokenEvent.amount.toString(), requestedDenominationTokenAmount, 'payment amount does not match') - assert.equal(denominationTokenEvent.paymentReference, 'Payroll', 'payment reference does not match') - - const anotherTokenEvent = events.find(e => e.args.token === anotherToken.address).args - assert.equal(anotherTokenEvent.employee, employee, 'employee address does not match') - assert.equal(anotherTokenEvent.token, anotherToken.address, 'token address does not match') - assert.equal(anotherTokenEvent.amount.div(anotherTokenRate).trunc().toString(), requestedAnotherTokenAmount, 'payment amount does not match') - assert.equal(anotherTokenEvent.paymentReference, 'Payroll', 'payment reference does not match') + const eventDAI = events.find(e => e.args.token === DAI.address).args + assert.equal(eventDAI.employee, employee, 'employee address does not match') + assert.equal(eventDAI.token, DAI.address, 'DAI address does not match') + assert.equal(eventDAI.amount.toString(), requestedDAI, 'payment amount does not match') + assert.equal(eventDAI.paymentReference, 'Payroll', 'payment reference does not match') + + const eventANT = events.find(e => e.args.token === ANT.address).args + assert.equal(eventANT.employee, employee, 'employee address does not match') + assert.equal(eventANT.token, ANT.address, 'ANT address does not match') + assert.equal(eventANT.amount.toString(), requestedANT, 'payment amount does not match') + assert.equal(eventANT.paymentReference, 'Payroll', 'payment reference does not match') }) it('can be called multiple times between periods of time', async () => { // terminate employee in the future to ensure we can request payroll multiple times await payroll.terminateEmployee(employeeId, NOW + TWO_MONTHS + TWO_MONTHS, { from: owner }) - const previousDenominationTokenBalance = await denominationToken.balanceOf(employee) - const previousAnotherTokenBalance = await anotherToken.balanceOf(employee) + const previousDAI = await DAI.balanceOf(employee) + const previousANT = await ANT.balanceOf(employee) await payroll.payday(PAYMENT_TYPES.PAYROLL, requestedAmount, { from }) await increaseTime(ONE_MONTH) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate]) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE]) await payroll.payday(PAYMENT_TYPES.PAYROLL, requestedAmount, { from }) - const currentDenominationTokenBalance = await denominationToken.balanceOf(employee) - const expectedDenominationTokenBalance = previousDenominationTokenBalance.plus(requestedDenominationTokenAmount * 2) - assert.equal(currentDenominationTokenBalance.toString(), expectedDenominationTokenBalance.toString(), 'current denomination token balance does not match') + const currentDAI = await DAI.balanceOf(employee) + const expectedDAI = previousDAI.plus(requestedDAI * 2) + assert.equal(currentDAI.toString(), expectedDAI.toString(), 'current DAI balance does not match') - const currentAnotherTokenBalance = await anotherToken.balanceOf(employee) - const expectedAnotherTokenBalance = anotherTokenRate.mul(requestedAnotherTokenAmount * 2).plus(previousAnotherTokenBalance) - assert.equal(currentAnotherTokenBalance.toString(), expectedAnotherTokenBalance.toString(), 'current token balance does not match') + const currentANT = await ANT.balanceOf(employee) + const expectedANT = previousANT.plus(requestedANT * 2) + assert.equal(currentANT.toString(), expectedANT.toString(), 'current ANT balance does not match') }) } @@ -713,7 +706,7 @@ contract('Payroll payday', ([owner, employee, anyone]) => { context('when exchange rates are expired', () => { beforeEach('expire exchange rates', async () => { const expiredTimestamp = (await payroll.getTimestampPublic()).sub(RATE_EXPIRATION_TIME + 1) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate], expiredTimestamp) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE], expiredTimestamp) }) it('reverts', async () => { @@ -779,7 +772,7 @@ contract('Payroll payday', ([owner, employee, anyone]) => { context('when the employee did not set any token allocations yet', () => { context('when the employee has some pending salary', () => { - const owedSalary = maxUint256() + const owedSalary = MAX_UINT256 beforeEach('accumulate some pending salary', async () => { await increaseTime(ONE_MONTH) diff --git a/future-apps/payroll/test/contracts/Payroll_rates.test.js b/future-apps/payroll/test/contracts/Payroll_rates.test.js index 3403fd18b2..f75f88f9f3 100644 --- a/future-apps/payroll/test/contracts/Payroll_rates.test.js +++ b/future-apps/payroll/test/contracts/Payroll_rates.test.js @@ -1,22 +1,12 @@ const PAYMENT_TYPES = require('../helpers/payment_types') -const { bigExp, bn } = require('../helpers/numbers')(web3) +const { bn } = require('../helpers/numbers')(web3) const { getEventArgument } = require('../helpers/events') -const { deployErc20TokenAndDeposit, deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { NOW, TWO_MINUTES, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) +const { USD, ETH, ETH_RATE, deployDAI, DAI_RATE, deployANT, ANT_RATE, setTokenRate } = require('../helpers/tokens.js')(artifacts, web3) contract('Payroll rates handling,', ([owner, employee]) => { - let USD, ETH, DAI, ANT - let dao, payroll, payrollBase, finance, vault, priceFeed, employeeId - - const NOW = 1553703809 // random fixed timestamp in seconds - const TWO_MINUTES = 60 * 2 - const ONE_MONTH = 60 * 60 * 24 * 31 - const RATE_EXPIRATION_TIME = ONE_MONTH - - const TOKEN_DECIMALS = 18 - const ONE = bigExp(1, 18) - const SIG = '00'.repeat(65) // sig full of 0s - - const formatRate = (n) => bn(n.toFixed(18)).times(ONE) + let dao, payroll, payrollBase, finance, vault, priceFeed, employeeId, DAI, ANT const increaseTime = async seconds => { await payroll.mockIncreaseTime(seconds) @@ -26,12 +16,9 @@ contract('Payroll rates handling,', ([owner, employee]) => { before('deploy contracts and tokens', async () => { ({ dao, finance, vault, payrollBase } = await deployContracts(owner)) - USD = '0x0000000000000000000000000000000000000001' - ETH = '0x0000000000000000000000000000000000000000' + DAI = await deployDAI(owner, finance) + ANT = await deployANT(owner, finance) await finance.deposit(ETH, bn(50, 18), 'Initial ETH deposit', { from: owner, value: bn(50, 18) }) - - DAI = await deployErc20TokenAndDeposit(owner, finance, 'DAI', TOKEN_DECIMALS) - ANT = await deployErc20TokenAndDeposit(owner, finance, 'ANT', TOKEN_DECIMALS) }) beforeEach('initialize payroll app with USD as denomination token', async () => { @@ -41,13 +28,9 @@ contract('Payroll rates handling,', ([owner, employee]) => { describe('rates', () => { beforeEach('set rates and allow tokens', async () => { - const DAI_rate = bn(1) - const ANT_rate = bn(0.5) - const ETH_rate = bn(20) - - await priceFeed.update(ETH, USD, formatRate(ETH_rate), NOW, SIG) - await priceFeed.update(DAI.address, USD, formatRate(DAI_rate), NOW, SIG) - await priceFeed.update(ANT.address, USD, formatRate(ANT_rate), NOW, SIG) + await setTokenRate(priceFeed, ETH, USD, ETH_RATE) + await setTokenRate(priceFeed, DAI, USD, DAI_RATE) + await setTokenRate(priceFeed, ANT, USD, ANT_RATE) await payroll.addAllowedToken(ETH, { from: owner }) await payroll.addAllowedToken(DAI.address, { from: owner }) diff --git a/future-apps/payroll/test/contracts/Payroll_reentrancy.test.js b/future-apps/payroll/test/contracts/Payroll_reentrancy.test.js index b210c5dd50..39f1d54f9c 100644 --- a/future-apps/payroll/test/contracts/Payroll_reentrancy.test.js +++ b/future-apps/payroll/test/contracts/Payroll_reentrancy.test.js @@ -1,6 +1,7 @@ const { bigExp } = require('../helpers/numbers')(web3) const { assertRevert } = require('@aragon/test-helpers/assertThrow') -const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { NOW, ONE_MONTH, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) const MaliciousERC20 = artifacts.require('MaliciousERC20') const MaliciousEmployee = artifacts.require('MaliciousEmployee') @@ -8,11 +9,6 @@ const MaliciousEmployee = artifacts.require('MaliciousEmployee') contract('Payroll reentrancy guards', ([owner]) => { let dao, payroll, payrollBase, finance, vault, priceFeed, maliciousToken, employee - const NOW = 1553703809 // random fixed timestamp in seconds - const ONE_MONTH = 60 * 60 * 24 * 31 - const TWO_MONTHS = ONE_MONTH * 2 - const RATE_EXPIRATION_TIME = TWO_MONTHS - const REENTRANCY_ACTIONS = { PAYDAY: 0, CHANGE_ADDRESS: 1, SET_ALLOCATION: 2 } const increaseTime = async seconds => { diff --git a/future-apps/payroll/test/contracts/Payroll_reimbursements.test.js b/future-apps/payroll/test/contracts/Payroll_reimbursements.test.js index 0674e39709..ec9c36fda7 100644 --- a/future-apps/payroll/test/contracts/Payroll_reimbursements.test.js +++ b/future-apps/payroll/test/contracts/Payroll_reimbursements.test.js @@ -1,19 +1,13 @@ const PAYMENT_TYPES = require('../helpers/payment_types') -const setTokenRates = require('../helpers/set_token_rates')(web3) const { assertRevert } = require('@aragon/test-helpers/assertThrow') -const { bn, maxUint256 } = require('../helpers/numbers')(web3) +const { MAX_UINT256 } = require('../helpers/numbers')(web3) const { getEvents, getEventArgument } = require('../helpers/events') -const { deployErc20TokenAndDeposit, deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { NOW, ONE_MONTH, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) +const { USD, deployDAI, deployANT, DAI_RATE, ANT_RATE, setTokenRates } = require('../helpers/tokens.js')(artifacts, web3) contract('Payroll reimbursements', ([owner, employee, anyone]) => { - let dao, payroll, payrollBase, finance, vault, priceFeed, denominationToken, anotherToken, anotherTokenRate - - const NOW = 1553703809 // random fixed timestamp in seconds - const ONE_MONTH = 60 * 60 * 24 * 31 - const TWO_MONTHS = ONE_MONTH * 2 - const RATE_EXPIRATION_TIME = TWO_MONTHS - - const TOKEN_DECIMALS = 18 + let dao, payroll, payrollBase, finance, vault, priceFeed, DAI, ANT const increaseTime = async seconds => { await payroll.mockIncreaseTime(seconds) @@ -22,8 +16,8 @@ contract('Payroll reimbursements', ([owner, employee, anyone]) => { before('deploy base apps and tokens', async () => { ({ dao, finance, vault, payrollBase } = await deployContracts(owner)) - anotherToken = await deployErc20TokenAndDeposit(owner, finance, 'Another token', TOKEN_DECIMALS) - denominationToken = await deployErc20TokenAndDeposit(owner, finance, 'Denomination Token', TOKEN_DECIMALS) + ANT = await deployANT(owner, finance) + DAI = await deployDAI(owner, finance) }) beforeEach('create payroll and price feed instance', async () => { @@ -33,7 +27,7 @@ contract('Payroll reimbursements', ([owner, employee, anyone]) => { describe('addReimbursement', () => { context('when it has already been initialized', function () { beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the sender has permissions', () => { @@ -80,7 +74,7 @@ contract('Payroll reimbursements', ([owner, employee, anyone]) => { }) context('when the given value way greater than zero', () => { - const value = maxUint256() + const value = MAX_UINT256 it('reverts', async () => { await payroll.addReimbursement(employeeId, 1, { from }) @@ -135,12 +129,11 @@ contract('Payroll reimbursements', ([owner, employee, anyone]) => { describe('reimbursements payday', () => { context('when it has already been initialized', function () { beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) beforeEach('set token rates', async () => { - anotherTokenRate = bn(5) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate]) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE]) }) context('when the sender is an employee', () => { @@ -155,13 +148,13 @@ contract('Payroll reimbursements', ([owner, employee, anyone]) => { }) context('when the employee has already set some token allocations', () => { - const denominationTokenAllocation = 80 - const anotherTokenAllocation = 20 + const allocationDAI = 80 + const allocationANT = 20 beforeEach('set tokens allocation', async () => { - await payroll.addAllowedToken(anotherToken.address, { from: owner }) - await payroll.addAllowedToken(denominationToken.address, { from: owner }) - await payroll.determineAllocation([denominationToken.address, anotherToken.address], [denominationTokenAllocation, anotherTokenAllocation], { from }) + await payroll.addAllowedToken(ANT.address, { from: owner }) + await payroll.addAllowedToken(DAI.address, { from: owner }) + await payroll.determineAllocation([DAI.address, ANT.address], [allocationDAI, allocationANT], { from }) }) context('when the employee has some pending reimbursements', () => { @@ -173,22 +166,22 @@ contract('Payroll reimbursements', ([owner, employee, anyone]) => { }) const assertTransferredAmounts = (requestedAmount, expectedRequestedAmount = requestedAmount) => { - const requestedDenominationTokenAmount = parseInt(expectedRequestedAmount * denominationTokenAllocation / 100) - const requestedAnotherTokenAmount = expectedRequestedAmount * anotherTokenAllocation / 100 + const requestedDAI = DAI_RATE.mul(expectedRequestedAmount * allocationDAI / 100).trunc() + const requestedANT = ANT_RATE.mul(expectedRequestedAmount * allocationANT / 100).trunc() it('transfers all the pending reimbursements', async () => { - const previousDenominationTokenBalance = await denominationToken.balanceOf(employee) - const previousAnotherTokenBalance = await anotherToken.balanceOf(employee) + const previousDAI = await DAI.balanceOf(employee) + const previousANT = await ANT.balanceOf(employee) await payroll.payday(PAYMENT_TYPES.REIMBURSEMENT, requestedAmount, { from }) - const currentDenominationTokenBalance = await denominationToken.balanceOf(employee) - const expectedDenominationTokenBalance = previousDenominationTokenBalance.plus(requestedDenominationTokenAmount); - assert.equal(currentDenominationTokenBalance.toString(), expectedDenominationTokenBalance.toString(), 'current denomination token balance does not match') + const currentDAI = await DAI.balanceOf(employee) + const expectedDAI = previousDAI.plus(requestedDAI); + assert.equal(currentDAI.toString(), expectedDAI.toString(), 'current DAI balance does not match') - const currentAnotherTokenBalance = await anotherToken.balanceOf(employee) - const expectedAnotherTokenBalance = anotherTokenRate.mul(requestedAnotherTokenAmount).plus(previousAnotherTokenBalance).trunc() - assert.equal(currentAnotherTokenBalance.toString(), expectedAnotherTokenBalance.toString(), 'current token balance does not match') + const currentANT = await ANT.balanceOf(employee) + const expectedANT = previousANT.plus(requestedANT) + assert.equal(currentANT.toString(), expectedANT.toString(), 'current ANT balance does not match') }) it('emits one event per allocated token', async () => { @@ -197,17 +190,17 @@ contract('Payroll reimbursements', ([owner, employee, anyone]) => { const events = receipt.logs.filter(l => l.event === 'SendPayment') assert.equal(events.length, 2, 'should have emitted two events') - const denominationTokenEvent = events.find(e => e.args.token === denominationToken.address).args - assert.equal(denominationTokenEvent.employee, employee, 'employee address does not match') - assert.equal(denominationTokenEvent.token, denominationToken.address, 'denomination token address does not match') - assert.equal(denominationTokenEvent.amount.toString(), requestedDenominationTokenAmount, 'payment amount does not match') - assert.equal(denominationTokenEvent.paymentReference, 'Reimbursement', 'payment reference does not match') - - const anotherTokenEvent = events.find(e => e.args.token === anotherToken.address).args - assert.equal(anotherTokenEvent.employee, employee, 'employee address does not match') - assert.equal(anotherTokenEvent.token, anotherToken.address, 'token address does not match') - assert.equal(anotherTokenEvent.amount.div(anotherTokenRate).trunc().toString(), parseInt(requestedAnotherTokenAmount), 'payment amount does not match') - assert.equal(anotherTokenEvent.paymentReference, 'Reimbursement', 'payment reference does not match') + const eventDAI = events.find(e => e.args.token === DAI.address).args + assert.equal(eventDAI.employee, employee, 'employee address does not match') + assert.equal(eventDAI.token, DAI.address, 'DAI address does not match') + assert.equal(eventDAI.amount.toString(), requestedDAI, 'payment amount does not match') + assert.equal(eventDAI.paymentReference, 'Reimbursement', 'payment reference does not match') + + const eventANT = events.find(e => e.args.token === ANT.address).args + assert.equal(eventANT.employee, employee, 'employee address does not match') + assert.equal(eventANT.token, ANT.address, 'token address does not match') + assert.equal(eventANT.amount.toString(), requestedANT, 'payment amount does not match') + assert.equal(eventANT.paymentReference, 'Reimbursement', 'payment reference does not match') }) } @@ -233,7 +226,7 @@ contract('Payroll reimbursements', ([owner, employee, anyone]) => { context('when exchange rates are expired', () => { beforeEach('expire exchange rates', async () => { const expiredTimestamp = (await payroll.getTimestampPublic()).sub(RATE_EXPIRATION_TIME + 1) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate], expiredTimestamp) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE], expiredTimestamp) }) it('reverts', async () => { @@ -286,7 +279,7 @@ contract('Payroll reimbursements', ([owner, employee, anyone]) => { context('when exchange rates are expired', () => { beforeEach('expire exchange rates', async () => { const expiredTimestamp = (await payroll.getTimestampPublic()).sub(RATE_EXPIRATION_TIME + 1) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate], expiredTimestamp) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE], expiredTimestamp) }) it('reverts', async () => { @@ -377,7 +370,7 @@ contract('Payroll reimbursements', ([owner, employee, anyone]) => { context('when exchange rates are expired', () => { beforeEach('expire exchange rates', async () => { const expiredTimestamp = (await payroll.getTimestampPublic()).sub(RATE_EXPIRATION_TIME + 1) - await setTokenRates(priceFeed, denominationToken, [anotherToken], [anotherTokenRate], expiredTimestamp) + await setTokenRates(priceFeed, USD, [DAI, ANT], [DAI_RATE, ANT_RATE], expiredTimestamp) }) it('reverts', async () => { diff --git a/future-apps/payroll/test/contracts/Payroll_settings.test.js b/future-apps/payroll/test/contracts/Payroll_settings.test.js index 43cb3ac8c8..37604128b5 100644 --- a/future-apps/payroll/test/contracts/Payroll_settings.test.js +++ b/future-apps/payroll/test/contracts/Payroll_settings.test.js @@ -1,23 +1,18 @@ +const { USD } = require('../helpers/tokens')(artifacts, web3) const { getEvents } = require('../helpers/events') const { assertRevert } = require('@aragon/test-helpers/assertThrow') -const { deployErc20TokenAndDeposit, deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { NOW, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) const PriceFeed = artifacts.require('PriceFeedMock') const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' contract('Payroll settings', ([owner, anyone]) => { - let dao, payroll, payrollBase, finance, vault, priceFeed, denominationToken + let dao, payroll, payrollBase, finance, vault, priceFeed - const NOW = 1553703809 // random fixed timestamp in seconds - const ONE_MONTH = 60 * 60 * 24 * 31 - const TWO_MONTHS = ONE_MONTH * 2 - const RATE_EXPIRATION_TIME = TWO_MONTHS - - const TOKEN_DECIMALS = 18 before('deploy base apps and tokens', async () => { ({ dao, finance, vault, payrollBase } = await deployContracts(owner)) - denominationToken = await deployErc20TokenAndDeposit(owner, finance, 'Denomination Token', TOKEN_DECIMALS) }) beforeEach('create payroll and price feed instance', async () => { @@ -32,8 +27,8 @@ contract('Payroll settings', ([owner, anyone]) => { }) context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the sender has permissions', async () => { @@ -86,8 +81,8 @@ contract('Payroll settings', ([owner, anyone]) => { describe('setRateExpiryTime', () => { context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the sender has permissions', async () => { diff --git a/future-apps/payroll/test/contracts/Payroll_terminate_employee.test.js b/future-apps/payroll/test/contracts/Payroll_terminate_employee.test.js index 89fcce510d..e468001e33 100644 --- a/future-apps/payroll/test/contracts/Payroll_terminate_employee.test.js +++ b/future-apps/payroll/test/contracts/Payroll_terminate_employee.test.js @@ -1,18 +1,13 @@ const PAYMENT_TYPES = require('../helpers/payment_types') const { assertRevert } = require('@aragon/test-helpers/assertThrow') const { getEvents, getEventArgument } = require('../helpers/events') -const { bn, maxUint64, annualSalaryPerSecond } = require('../helpers/numbers')(web3) -const { deployErc20TokenAndDeposit, deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { NOW, ONE_MONTH, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { bn, MAX_UINT64, annualSalaryPerSecond } = require('../helpers/numbers')(web3) +const { USD, DAI_RATE, deployDAI, setTokenRate } = require('../helpers/tokens.js')(artifacts, web3) +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) contract('Payroll employees termination', ([owner, employee, anyone]) => { - let dao, payroll, payrollBase, finance, vault, priceFeed, denominationToken - - const NOW = 1553703809 // random fixed timestamp in seconds - const ONE_MONTH = 60 * 60 * 24 * 31 - const TWO_MONTHS = ONE_MONTH * 2 - const RATE_EXPIRATION_TIME = TWO_MONTHS - - const TOKEN_DECIMALS = 18 + let dao, payroll, payrollBase, finance, vault, priceFeed, DAI const currentTimestamp = async () => payroll.getTimestampPublic() @@ -23,7 +18,7 @@ contract('Payroll employees termination', ([owner, employee, anyone]) => { before('deploy base apps and tokens', async () => { ({ dao, finance, vault, payrollBase } = await deployContracts(owner)) - denominationToken = await deployErc20TokenAndDeposit(owner, finance, 'Denomination Token', TOKEN_DECIMALS) + DAI = await deployDAI(owner, finance) }) beforeEach('create payroll and price feed instance', async () => { @@ -32,13 +27,13 @@ contract('Payroll employees termination', ([owner, employee, anyone]) => { describe('terminateEmployee', () => { context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the given employee id exists', () => { let employeeId - const salary = annualSalaryPerSecond(100000, TOKEN_DECIMALS) + const salary = annualSalaryPerSecond(100000) beforeEach('add employee', async () => { const receipt = await payroll.addEmployee(employee, salary, 'Boss', await payroll.getTimestampPublic(), { from: owner }) @@ -51,8 +46,9 @@ contract('Payroll employees termination', ([owner, employee, anyone]) => { context('when the employee was not terminated', () => { let endDate - beforeEach('allowed denomination token', async () => { - await payroll.addAllowedToken(denominationToken.address, { from: owner }) + beforeEach('allow DAI and set rate', async () => { + await setTokenRate(priceFeed, USD, DAI, DAI_RATE) + await payroll.addAllowedToken(DAI.address, { from: owner }) }) context('when the given end date is in the future ', () => { @@ -80,8 +76,8 @@ contract('Payroll employees termination', ([owner, employee, anyone]) => { }) it('does not reset the owed salary nor the reimbursements of the employee', async () => { - const previousBalance = await denominationToken.balanceOf(employee) - await payroll.determineAllocation([denominationToken.address], [100], { from: employee }) + const previousDAI = await DAI.balanceOf(employee) + await payroll.determineAllocation([DAI.address], [100], { from: employee }) // Accrue some salary and extras await increaseTime(ONE_MONTH) @@ -91,25 +87,25 @@ contract('Payroll employees termination', ([owner, employee, anyone]) => { // Terminate employee and travel some time in the future await payroll.terminateEmployee(employeeId, endDate, { from }) - await increaseTime(ONE_MONTH) + await increaseTime(ONE_MONTH - 1) // to avoid expire rates // Request owed money await payroll.payday(PAYMENT_TYPES.PAYROLL, 0, { from: employee }) await payroll.payday(PAYMENT_TYPES.REIMBURSEMENT, 0, { from: employee }) await assertRevert(payroll.getEmployee(employeeId), 'PAYROLL_EMPLOYEE_DOESNT_EXIST') - const currentBalance = await denominationToken.balanceOf(employee) - const expectedCurrentBalance = previousBalance.plus(owedSalary).plus(reimbursement) - assert.equal(currentBalance.toString(), expectedCurrentBalance.toString(), 'current balance does not match') + const currentDAI = await DAI.balanceOf(employee) + const expectedDAI = previousDAI.plus(owedSalary).plus(reimbursement) + assert.equal(currentDAI.toString(), expectedDAI.toString(), 'current balance does not match') }) it('can re-add a removed employee', async () => { - await payroll.determineAllocation([denominationToken.address], [100], { from: employee }) + await payroll.determineAllocation([DAI.address], [100], { from: employee }) await increaseTime(ONE_MONTH) // Terminate employee and travel some time in the future await payroll.terminateEmployee(employeeId, endDate, { from }) - await increaseTime(ONE_MONTH) + await increaseTime(ONE_MONTH - 1) // to avoid expire rates // Request owed money await payroll.payday(PAYMENT_TYPES.PAYROLL, 0, { from: employee }) @@ -126,7 +122,7 @@ contract('Payroll employees termination', ([owner, employee, anyone]) => { assert.equal(bonus.toString(), 0, 'employee bonus does not match') assert.equal(reimbursements.toString(), 0, 'employee reimbursements does not match') assert.equal(accruedSalary.toString(), 0, 'employee accrued salary does not match') - assert.equal(date.toString(), maxUint64(), 'employee end date does not match') + assert.equal(date.toString(), MAX_UINT64, 'employee end date does not match') }) }) diff --git a/future-apps/payroll/test/contracts/Payroll_token_allocations.test.js b/future-apps/payroll/test/contracts/Payroll_token_allocations.test.js index 906b7104a8..2412a74c8f 100644 --- a/future-apps/payroll/test/contracts/Payroll_token_allocations.test.js +++ b/future-apps/payroll/test/contracts/Payroll_token_allocations.test.js @@ -1,22 +1,17 @@ const { assertRevert } = require('@aragon/test-helpers/assertThrow') const { getEvents, getEventArgument } = require('../helpers/events') -const { deployErc20TokenAndDeposit, deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy.js')(artifacts, web3) +const { NOW, ONE_MONTH, RATE_EXPIRATION_TIME } = require('../helpers/time') +const { USD, deployDAI, deployTokenAndDeposit } = require('../helpers/tokens.js')(artifacts, web3) +const { deployContracts, createPayrollAndPriceFeed } = require('../helpers/deploy')(artifacts, web3) const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' contract('Payroll token allocations', ([owner, employee, anyone]) => { - let dao, payroll, payrollBase, finance, vault, priceFeed, denominationToken - - const NOW = 1553703809 // random fixed timestamp in seconds - const ONE_MONTH = 60 * 60 * 24 * 31 - const TWO_MONTHS = ONE_MONTH * 2 - const RATE_EXPIRATION_TIME = TWO_MONTHS - - const TOKEN_DECIMALS = 18 + let dao, payroll, payrollBase, finance, vault, priceFeed, DAI before('deploy base apps and tokens', async () => { ({ dao, finance, vault, payrollBase } = await deployContracts(owner)) - denominationToken = await deployErc20TokenAndDeposit(owner, finance, 'Denomination Token', TOKEN_DECIMALS) + DAI = await deployDAI(owner, finance) }) beforeEach('create payroll and price feed instance', async () => { @@ -27,15 +22,15 @@ contract('Payroll token allocations', ([owner, employee, anyone]) => { const tokenAddresses = [] before('deploy some tokens', async () => { - const token1 = await deployErc20TokenAndDeposit(owner, finance, 'Token 1', 14) - const token2 = await deployErc20TokenAndDeposit(owner, finance, 'Token 2', 14) - const token3 = await deployErc20TokenAndDeposit(owner, finance, 'Token 3', 14) + const token1 = await deployTokenAndDeposit(owner, finance, 'Token 1', 14) + const token2 = await deployTokenAndDeposit(owner, finance, 'Token 2', 14) + const token3 = await deployTokenAndDeposit(owner, finance, 'Token 3', 14) tokenAddresses.push(token1.address, token2.address, token3.address) }) context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) beforeEach('allow multiple tokens', async () => { @@ -80,7 +75,7 @@ contract('Payroll token allocations', ([owner, employee, anyone]) => { let token beforeEach('submit previous allocation', async () => { - token = await deployErc20TokenAndDeposit(owner, finance, 'Previous Token', 18) + token = await deployTokenAndDeposit(owner, finance, 'Previous Token', 18) await payroll.addAllowedToken(token.address, { from: owner }) await payroll.determineAllocation([token.address], [100], { from }) @@ -138,7 +133,7 @@ contract('Payroll token allocations', ([owner, employee, anyone]) => { let notAllowedToken beforeEach('deploy new token', async () => { - notAllowedToken = await deployErc20TokenAndDeposit(owner, finance, 'Not-allowed token', 14) + notAllowedToken = await deployTokenAndDeposit(owner, finance, 'Not-allowed token', 14) }) it('reverts', async () => { @@ -201,8 +196,8 @@ contract('Payroll token allocations', ([owner, employee, anyone]) => { describe('getAllocation', () => { context('when it has already been initialized', function () { - beforeEach('initialize payroll app', async () => { - await payroll.initialize(finance.address, denominationToken.address, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) + beforeEach('initialize payroll app using USD as denomination token', async () => { + await payroll.initialize(finance.address, USD, priceFeed.address, RATE_EXPIRATION_TIME, { from: owner }) }) context('when the employee exists', () => { @@ -217,23 +212,23 @@ contract('Payroll token allocations', ([owner, employee, anyone]) => { context('when the given token is not the zero address', () => { context('when the given token was allowed', () => { beforeEach('allow denomination token', async () => { - await payroll.addAllowedToken(denominationToken.address, { from: owner }) + await payroll.addAllowedToken(DAI.address, { from: owner }) }) context('when the given token was picked by the employee', () => { beforeEach('determine allocation', async () => { - await payroll.determineAllocation([denominationToken.address], [100], { from: employee }) + await payroll.determineAllocation([DAI.address], [100], { from: employee }) }) it('tells its corresponding allocation', async () => { - const allocation = await payroll.getAllocation(employeeId, denominationToken.address) + const allocation = await payroll.getAllocation(employeeId, DAI.address) assert.equal(allocation.toString(), 100, 'token allocation does not match') }) }) context('when the given token was not picked by the employee', () => { it('returns 0', async () => { - const allocation = await payroll.getAllocation(employeeId, denominationToken.address) + const allocation = await payroll.getAllocation(employeeId, DAI.address) assert.equal(allocation.toString(), 0, 'token allocation should be zero') }) }) @@ -241,7 +236,7 @@ contract('Payroll token allocations', ([owner, employee, anyone]) => { context('when the given token was not allowed', () => { it('returns 0', async () => { - const allocation = await payroll.getAllocation(employeeId, denominationToken.address) + const allocation = await payroll.getAllocation(employeeId, DAI.address) assert.equal(allocation.toString(), 0, 'token allocation should be zero') }) }) @@ -301,7 +296,7 @@ contract('Payroll token allocations', ([owner, employee, anyone]) => { const employeeId = 0 it('reverts', async () => { - await assertRevert(payroll.getAllocation(employeeId, denominationToken.address), 'PAYROLL_EMPLOYEE_DOESNT_EXIST') + await assertRevert(payroll.getAllocation(employeeId, DAI.address), 'PAYROLL_EMPLOYEE_DOESNT_EXIST') }) }) }) @@ -310,7 +305,7 @@ contract('Payroll token allocations', ([owner, employee, anyone]) => { const employeeId = 0 it('reverts', async () => { - await assertRevert(payroll.getAllocation(employeeId, denominationToken.address), 'PAYROLL_EMPLOYEE_DOESNT_EXIST') + await assertRevert(payroll.getAllocation(employeeId, DAI.address), 'PAYROLL_EMPLOYEE_DOESNT_EXIST') }) }) }) diff --git a/future-apps/payroll/test/helpers/deploy.js b/future-apps/payroll/test/helpers/deploy.js index 68b8c5d7a9..f1083399ec 100644 --- a/future-apps/payroll/test/helpers/deploy.js +++ b/future-apps/payroll/test/helpers/deploy.js @@ -1,6 +1,8 @@ module.exports = (artifacts, web3) => { const { bigExp } = require('./numbers')(web3) const { getEventArgument } = require('./events') + const { SECONDS_IN_A_YEAR } = require('./time') + const getContract = name => artifacts.require(name) const ACL = getContract('ACL') @@ -59,7 +61,6 @@ module.exports = (artifacts, web3) => { await acl.createPermission(finance.address, vault.address, TRANSFER_ROLE, owner, { from: owner }) await vault.initialize() - const SECONDS_IN_A_YEAR = 31557600 // 365.25 days await finance.initialize(vault.address, SECONDS_IN_A_YEAR) // more than one day const payrollBase = await Payroll.new() diff --git a/future-apps/payroll/test/helpers/numbers.js b/future-apps/payroll/test/helpers/numbers.js index ff2b331a20..f672d96bef 100644 --- a/future-apps/payroll/test/helpers/numbers.js +++ b/future-apps/payroll/test/helpers/numbers.js @@ -1,20 +1,21 @@ -const SECONDS_IN_A_YEAR = 31557600 // 365.25 days +const { SECONDS_IN_A_YEAR } = require('./time') module.exports = web3 => { const bn = x => new web3.BigNumber(x) const bigExp = (x, y) => bn(x).mul(bn(10).pow(bn(y))) - const maxUint = (e) => bn(2).pow(bn(e)).sub(bn(1)) - const maxUint64 = () => maxUint(64) - const maxUint256 = () => maxUint(256) + const annualSalaryPerSecond = (amount, decimals = 18) => bigExp(amount, decimals).dividedToIntegerBy(SECONDS_IN_A_YEAR) - const annualSalaryPerSecond = (amount, decimals) => bigExp(amount, decimals).dividedToIntegerBy(SECONDS_IN_A_YEAR) + const ONE = bigExp(1, 18) + const MAX_UINT64 = maxUint(64) + const MAX_UINT256 = maxUint(256) return { bn, bigExp, - maxUint64, - maxUint256, - annualSalaryPerSecond + annualSalaryPerSecond, + ONE, + MAX_UINT64, + MAX_UINT256, } } diff --git a/future-apps/payroll/test/helpers/set_token_rates.js b/future-apps/payroll/test/helpers/set_token_rates.js deleted file mode 100644 index 61c7f3b41c..0000000000 --- a/future-apps/payroll/test/helpers/set_token_rates.js +++ /dev/null @@ -1,23 +0,0 @@ -const SIG = '00'.repeat(65) // sig full of 0s - -module.exports = web3 => { - - function formatRate(n) { - const { bn, bigExp } = require('./numbers')(web3) - const ONE = bigExp(1, 18) - return bn(n.toFixed(18)).times(ONE) - } - - return async function setTokenRates(feed, denominationToken, tokens, rates, when = undefined) { - if (!when) when = await feed.getTimestampPublic() - - const quotes = tokens.map(token => typeof(token) === 'object' ? token.address : token) - const bases = tokens.map(() => typeof(denominationToken) === 'object' ? denominationToken.address : denominationToken) - const xrts = rates.map(rate => formatRate(rate)) - const whens = tokens.map(() => when) - const sigs = `0x${SIG.repeat(tokens.length)}` - - return feed.updateMany(bases, quotes, xrts, whens, sigs) - } - -} diff --git a/future-apps/payroll/test/helpers/time.js b/future-apps/payroll/test/helpers/time.js new file mode 100644 index 0000000000..7b4c2df9b0 --- /dev/null +++ b/future-apps/payroll/test/helpers/time.js @@ -0,0 +1,18 @@ +const NOW = 1553703809 // random fixed timestamp in seconds +const ONE_MINUTE = 60 +const TWO_MINUTES = ONE_MINUTE * 2 +const ONE_MONTH = 60 * 60 * 24 * 31 +const TWO_MONTHS = ONE_MONTH * 2 +const SECONDS_IN_A_YEAR = 31557600 // 365.25 days + +const RATE_EXPIRATION_TIME = TWO_MONTHS + +module.exports = { + NOW, + ONE_MINUTE, + TWO_MINUTES, + ONE_MONTH, + TWO_MONTHS, + RATE_EXPIRATION_TIME, + SECONDS_IN_A_YEAR +} diff --git a/future-apps/payroll/test/helpers/tokens.js b/future-apps/payroll/test/helpers/tokens.js new file mode 100644 index 0000000000..9066555cf4 --- /dev/null +++ b/future-apps/payroll/test/helpers/tokens.js @@ -0,0 +1,62 @@ +const SIG = '00'.repeat(65) // sig full of 0s + +const ETH = '0x0000000000000000000000000000000000000000' +const USD = '0xFFFfFfffffFFFFFFFfffFFFFFffffFfFfFAAaCbB' // USD identifier: https://github.com/aragon/ppf#tickers-and-token-addresses + +module.exports = (artifacts, web3) => { + const { ONE, bn, bigExp } = require('./numbers')(web3) + + const formatRate = n => bn(n.toFixed(18)).times(ONE) + + const ETH_RATE = bn(20) // 1 ETH = 20 USD + const DAI_RATE = bn(1) // 1 DAI = 1 USD + const ANT_RATE = bn(0.5) // 1 ANT = 0.5 USD + + const deployANT = async (sender, finance) => deployTokenAndDeposit(sender, finance, 'ANT') + const deployDAI = async (sender, finance) => deployTokenAndDeposit(sender, finance, 'DAI') + + async function deployTokenAndDeposit(sender, finance, name = 'ERC20Token', decimals = 18) { + const MiniMeToken = artifacts.require('MiniMeToken') + const token = await MiniMeToken.new('0x0', '0x0', 0, name, decimals, 'E20', true) // dummy parameters for minime + const amount = bigExp(1e18, decimals) + await token.generateTokens(sender, amount) + await token.approve(finance.address, amount, { from: sender }) + await finance.deposit(token.address, amount, `Initial ${name} deposit`, { from: sender }) + return token + } + + async function setTokenRate(feed, denominationToken, token, rate, when = undefined) { + if (!when) when = await feed.getTimestampPublic() + + const base = typeof(token) === 'object' ? token.address : token + const quote = typeof(denominationToken) === 'object' ? denominationToken.address : denominationToken + const xrt = formatRate(rate) + + return feed.update(base, quote, xrt, when, SIG) + } + + async function setTokenRates(feed, denominationToken, tokens, rates, when = undefined) { + if (!when) when = await feed.getTimestampPublic() + + const bases = tokens.map(token => typeof(token) === 'object' ? token.address : token) + const quotes = tokens.map(() => typeof(denominationToken) === 'object' ? denominationToken.address : denominationToken) + const xrts = rates.map(rate => formatRate(rate)) + const whens = tokens.map(() => when) + const sigs = `0x${SIG.repeat(tokens.length)}` + + return feed.updateMany(bases, quotes, xrts, whens, sigs) + } + + return { + ETH, + USD, + ETH_RATE, + DAI_RATE, + ANT_RATE, + deployANT, + deployDAI, + deployTokenAndDeposit, + setTokenRate, + setTokenRates + } +}