diff --git a/contracts/CreditRule.sol b/contracts/CreditRule.sol new file mode 100644 index 0000000..ba3f727 --- /dev/null +++ b/contracts/CreditRule.sol @@ -0,0 +1,170 @@ +pragma solidity ^0.5.0; + +// Copyright 2018 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import "./SafeMath.sol"; +import "./TokenRules.sol"; + +contract CreditRule { + + /* Usings */ + + using SafeMath for uint256; + + + /* Struct */ + + struct CreditInfo { + uint256 amount; + bool inProgress; + } + + + /* Storage */ + + address public budgetHolder; + + TokenRules public tokenRules; + + mapping(address => CreditInfo) public credits; + + + /* Modifiers */ + + modifier onlyBudgetHolder() + { + require( + msg.sender == budgetHolder, + "Only budget holder is allowed to call." + ); + + _; + } + + + /* Special Functions */ + + constructor( + address _budgetHolder, + address _tokenRules + ) + public + { + require( + _budgetHolder != address(0), + "Budget holder's address is null." + ); + + require( + _tokenRules != address(0), + "Token rules's address is null." + ); + + budgetHolder = _budgetHolder; + + tokenRules = TokenRules(_tokenRules); + } + + + /* External Functions */ + + function executeRule( + uint256 _creditAmount, + address _to, // token holder address + bytes calldata _data // token holder execute rule data + ) + external + payable + onlyBudgetHolder + returns(bool executionStatus_) + { + require(_to != address(0)); + + CreditInfo storage c = credits[_to]; + + require( + !c.inProgress, + "Re-entrancy occured in crediting process." + ); + + c.inProgress = true; + c.amount = _creditAmount; + + bytes memory returnData; + // solium-disable-next-line security/no-call-value + (executionStatus_, returnData) = address(_to).call.value(msg.value)(_data); + + c.amount = 0; + c.inProgress = false; + } + + function executeTransfers( + address _from, + address[] calldata _transfersTo, + uint256[] calldata _transfersAmount + ) + external + { + if (credits[_from].inProgress) { + uint256 sumAmount = 0; + + for(uint256 i = 0; i < _transfersAmount.length; ++i) { + sumAmount = sumAmount.add(_transfersAmount[i]); + } + + uint256 creditAmount = credits[_from].amount; + + uint256 amountToTransferFromBudgetHolder = ( + sumAmount > creditAmount ? creditAmount : sumAmount + ); + + executeTransfer( + budgetHolder, + _from, + amountToTransferFromBudgetHolder + ); + } + + tokenRules.executeTransfers( + _from, + _transfersTo, + _transfersAmount + ); + } + + + /* Private Functions */ + + function executeTransfer( + address _from, + address _beneficiary, + uint256 _amount + ) + private + { + address[] memory transfersTo = new address[](1); + transfersTo[0] = _beneficiary; + + uint256[] memory transfersAmount = new uint256[](1); + transfersAmount[0] = _amount; + + tokenRules.executeTransfers( + _from, + transfersTo, + transfersAmount + ); + } + +} \ No newline at end of file diff --git a/contracts/test_doubles/unit_tests/TokenRulesSpy.sol b/contracts/test_doubles/unit_tests/TokenRulesSpy.sol index 7ec6833..192d0dd 100644 --- a/contracts/test_doubles/unit_tests/TokenRulesSpy.sol +++ b/contracts/test_doubles/unit_tests/TokenRulesSpy.sol @@ -1,18 +1,38 @@ pragma solidity ^0.5.0; +import "../../EIP20TokenInterface.sol"; + contract TokenRulesSpy { + /* Structs */ + + struct TransactionEntry { + address from; + address[] transfersTo; + uint256[] transfersAmount; + } + + /* Storage */ + EIP20TokenInterface public token; + + TransactionEntry[] public transactions; + + uint256 public transactionsLength; + mapping (address => bool) public allowedTransfers; - address public recordedFrom; - address[] public recordedTransfersTo; - uint256 public recordedTransfersToLength; + /* Special Functions */ + + constructor(EIP20TokenInterface _token) + public + { + require(address(_token) != address(0), "Token address is null."); - uint256[] public recordedTransfersAmount; - uint256 public recordedTransfersAmountLength; + token = _token; + } /* External Functions */ @@ -29,6 +49,30 @@ contract TokenRulesSpy { allowedTransfers[msg.sender] = false; } + function fromTransaction(uint256 index) + external + view + returns (address) + { + return transactions[index].from; + } + + function transfersToTransaction(uint256 index) + external + view + returns (address[] memory) + { + return transactions[index].transfersTo; + } + + function transfersAmountTransaction(uint256 index) + external + view + returns (uint256[] memory) + { + return transactions[index].transfersAmount; + } + function executeTransfers( address _from, address[] calldata _transfersTo, @@ -36,13 +80,25 @@ contract TokenRulesSpy { ) external { - recordedFrom = _from; + TransactionEntry memory entry = TransactionEntry({ + from: _from, + transfersTo: new address[](0), + transfersAmount: new uint256[](0) + }); + + transactions.push(entry); + + for (uint256 i = 0; i < _transfersTo.length; ++i) { + transactions[transactionsLength].transfersTo.push(_transfersTo[i]); + } - recordedTransfersTo = _transfersTo; - recordedTransfersToLength = _transfersTo.length; + for (uint256 i = 0; i < _transfersAmount.length; ++i) { + transactions[transactionsLength].transfersAmount.push( + _transfersAmount[i] + ); + } - recordedTransfersAmount = _transfersAmount; - recordedTransfersAmountLength = _transfersAmount.length; + ++transactionsLength; } } diff --git a/contracts/test_doubles/unit_tests/credit_rule/CustomRuleWithCredit.sol b/contracts/test_doubles/unit_tests/credit_rule/CustomRuleWithCredit.sol new file mode 100644 index 0000000..99bca29 --- /dev/null +++ b/contracts/test_doubles/unit_tests/credit_rule/CustomRuleWithCredit.sol @@ -0,0 +1,87 @@ +pragma solidity ^0.5.0; + +// Copyright 2018 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import "../../../SafeMath.sol"; +import "../../../CreditRule.sol"; + +contract CustomRuleWithCredit { + + /* Usings */ + + using SafeMath for uint256; + + event Pay( + address _to, + uint256 _amount + ); + + /* Storage */ + + CreditRule public creditRule; + + bool public markedToFail; + + + /* Special Functions */ + + constructor( + address _creditRule + ) + public + { + require( + address(_creditRule) != address(0), + "Credit rule's address is null." + ); + + creditRule = CreditRule(_creditRule); + } + + + /* External Functions */ + + function makeMeFail() + external + { + markedToFail = true; + } + + function pay( + address _to, + uint256 _amount + ) + external + { + require( + !markedToFail, + "The function is marked to fail." + ); + + address[] memory transfersTo = new address[](1); + transfersTo[0] = _to; + + uint256[] memory transfersAmount = new uint256[](1); + transfersAmount[0] = _amount; + + creditRule.executeTransfers( + msg.sender, + transfersTo, + transfersAmount + ); + + emit Pay(_to, _amount); + } +} \ No newline at end of file diff --git a/test/credit_rule/constructor.js b/test/credit_rule/constructor.js new file mode 100644 index 0000000..90f4824 --- /dev/null +++ b/test/credit_rule/constructor.js @@ -0,0 +1,69 @@ +// Copyright 2018 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const Utils = require('../test_lib/utils'); +const { AccountProvider } = require('../test_lib/utils'); + +const CreditRule = artifacts.require('CreditRule'); + +contract('CreditRule::constructor', async () => { + contract('Negative Tests', async (accounts) => { + const accountProvider = new AccountProvider(accounts); + + it('Reverts if the credit budget holder\'s address is null.', async () => { + await Utils.expectRevert( + CreditRule.new( + Utils.NULL_ADDRESS, // credit budget holder's address + accountProvider.get(), // token rules's address + ), + 'Should revert as the credit budget holder\'s address is null.', + 'Budget holder\'s address is null.', + ); + }); + + it('Reverts if the token rules\'s address is null.', async () => { + await Utils.expectRevert( + CreditRule.new( + accountProvider.get(), // credit budget holder's address + Utils.NULL_ADDRESS, // token rules's address + ), + 'Should revert as the token rules\'s address is null.', + 'Token rules\'s address is null.', + ); + }); + }); + + contract('Storage', async (accounts) => { + const accountProvider = new AccountProvider(accounts); + it('Checks that passed arguments are set correctly.', async () => { + const creditBudgetHolder = accountProvider.get(); + const tokenRules = accountProvider.get(); + + const creditRule = await CreditRule.new( + creditBudgetHolder, + tokenRules, + ); + + assert.strictEqual( + (await creditRule.budgetHolder.call()), + creditBudgetHolder, + ); + + assert.strictEqual( + (await creditRule.tokenRules.call()), + tokenRules, + ); + }); + }); +}); diff --git a/test/credit_rule/execute_transfers.js b/test/credit_rule/execute_transfers.js new file mode 100644 index 0000000..fc13f60 --- /dev/null +++ b/test/credit_rule/execute_transfers.js @@ -0,0 +1,301 @@ +// Copyright 2018 OpenST Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const web3 = require('../test_lib/web3.js'); +const Utils = require('../test_lib/utils.js'); +const { AccountProvider } = require('../test_lib/utils'); + +const CreditRule = artifacts.require('CreditRule'); +const TokenRulesSpy = artifacts.require('TokenRulesSpy'); +const EIP20TokenFake = artifacts.require('EIP20TokenFake'); +const TokenHolder = artifacts.require('TokenHolder'); +const CustomRuleWithCredit = artifacts.require('CustomRuleWithCredit'); + +const budgetHolderSessionPublicKey = '0xBbfd1BF77dA692abc82357aC001415b98d123d17'; +const budgetHolderSessionPrivateKey = '0x6817f551bbc3e12b8fe36787ab192c921390d6176a3324ed02f96935a370bc41'; +const tokenHolderSessionPublicKey = '0x62502C4DF73935D0D10054b0Fb8cC036534C6fb0'; +const tokenHolderSessionPrivateKey = '0xa8225c01ceeaf01d7bc7c1b1b929037bd4050967c5730c0b854263121b8399f3'; + +async function prepare( + accountProvider, + budgetHolderInitialBalance, + tokenHolderInitialBalance, +) { + const token = await EIP20TokenFake.new( + 'OST', 'Open Simple Token', 1, + ); + + const tokenRules = await TokenRulesSpy.new(token.address); + + const budgetHolderOwnerAddress = accountProvider.get(); + + const budgetHolder = await TokenHolder.new( + token.address, tokenRules.address, budgetHolderOwnerAddress, + ); + + await token.increaseBalance(budgetHolder.address, budgetHolderInitialBalance); + + const budgetHolderSessionKeySpendingLimit = 33; + const budgetHolderSessionKeyExpirationDelta = 300; + + await budgetHolder.authorizeSession( + budgetHolderSessionPublicKey, + budgetHolderSessionKeySpendingLimit, + (await web3.eth.getBlockNumber()) + budgetHolderSessionKeyExpirationDelta, + { from: budgetHolderOwnerAddress }, + ); + + const tokenHolderOwnerAddress = accountProvider.get(); + + const tokenHolder = await TokenHolder.new( + token.address, tokenRules.address, tokenHolderOwnerAddress, + ); + + await token.increaseBalance(tokenHolder.address, tokenHolderInitialBalance); + + const tokenHolderSessionKeySpendingLimit = 22; + const tokenHolderSessionKeyExpirationDelta = 200; + + await tokenHolder.authorizeSession( + tokenHolderSessionPublicKey, + tokenHolderSessionKeySpendingLimit, + (await web3.eth.getBlockNumber()) + tokenHolderSessionKeyExpirationDelta, + { from: tokenHolderOwnerAddress }, + ); + + const creditRule = await CreditRule.new( + budgetHolder.address, tokenRules.address, + ); + + const customRule = await CustomRuleWithCredit.new(creditRule.address); + + return { + token, + tokenRules, + budgetHolderOwnerAddress, + budgetHolder, + budgetHolderSessionKeySpendingLimit, + budgetHolderSessionKeyExpirationDelta, + tokenHolderOwnerAddress, + tokenHolder, + tokenHolderSessionKeySpendingLimit, + tokenHolderSessionKeyExpirationDelta, + creditRule, + customRule, + }; +} + +function generateTokenHolderExecuteRuleCallPrefix() { + return web3.eth.abi.encodeFunctionSignature({ + name: 'executeRule', + type: 'function', + inputs: [ + { + type: 'address', name: '', + }, + { + type: 'bytes', name: '', + }, + { + type: 'uint256', name: '', + }, + { + type: 'uint8', name: '', + }, + { + type: 'bytes32', name: '', + }, + { + type: 'bytes32', name: '', + }, + ], + }); +} + +async function checkTransactions( + tokenRulesSpy, + creditBudgetHolderAddr, + creditAmount, + userTokenHolderAddr, + beneficiaries, + amounts, +) { + const transactionsLength = await tokenRulesSpy.transactionsLength.call(); + + // Transactions count registered in TokenRulesSpy should be 2: + // - transfers from CreditBudgetHolder to UserTokenHolder + // - transfers from UserTokenHolder instance to beneficiaries + assert.isOk( + transactionsLength.eqn(2), + ); + + // First transfer (crediting) is from CreditBudgetHolder to UserTokenHolder. + assert.strictEqual( + await tokenRulesSpy.fromTransaction.call(0), + creditBudgetHolderAddr, + ); + + // UserTokenHolder address is the only beneficiary for the first transfer. + const firstTransfersToTransaction = await tokenRulesSpy.transfersToTransaction.call(0); + assert.strictEqual( + firstTransfersToTransaction.length, + 1, + ); + assert.strictEqual( + firstTransfersToTransaction[0], + userTokenHolderAddr, + ); + + // UserTokenHolder credited amount is calculated, by: + // min(sum(amounts), creditAmount) + const firstTransfersAmountTransaction = await tokenRulesSpy.transfersAmountTransaction.call(0); + assert.strictEqual( + firstTransfersAmountTransaction.length, + 1, + ); + assert.isOk( + (firstTransfersAmountTransaction[0]).eqn( + Math.min( + // Calculates sum of the amounts elements. + amounts.reduce((accumulator, currentValue) => accumulator + currentValue), + creditAmount, + ), + ), + ); + + // Second transfers is from UserTokenHolder to the beneficiaries. + assert.strictEqual( + await tokenRulesSpy.fromTransaction.call(1), + userTokenHolderAddr, + ); + + const secondTransfersToTransaction = await tokenRulesSpy.transfersToTransaction.call(1); + assert.strictEqual( + secondTransfersToTransaction.length, + beneficiaries.length, + ); + + for (let i = 0; i < secondTransfersToTransaction.length; i += 1) { + assert.strictEqual( + secondTransfersToTransaction[i], + beneficiaries[i], + ); + } + + const secondTransfersAmountTransaction = await tokenRulesSpy.transfersAmountTransaction.call(1); + assert.strictEqual( + secondTransfersAmountTransaction.length, + amounts.length, + ); + + for (let i = 0; i < secondTransfersAmountTransaction.length; i += 1) { + assert.isOk( + (secondTransfersAmountTransaction[i]).eqn( + amounts[i], + ), + ); + } +} + +contract('Credit::execute_transfers', async () => { + contract('Happy Path', async (accounts) => { + const accountProvider = new AccountProvider(accounts); + + it('Checks that passed arguments are set correctly.', async () => { + const { + tokenRules, + budgetHolder, + tokenHolder, + creditRule, + customRule, + } = await prepare( + accountProvider, + 222, // budgetHolderInitialBalance + 111, // tokenHolderInitialBalance + ); + + const beneficiaryAddress = accountProvider.get(); + const amount = 11; + + const customRuleWithCreditPayFunctionData = customRule.contract.methods.pay( + beneficiaryAddress, amount, + ).encodeABI(); + + const tokenHolderSessionKeyData = await tokenHolder.sessionKeys.call( + tokenHolderSessionPublicKey, + ); + + const { + exTxSignature: customRuleExTxSignature, + } = await Utils.generateExTx( + tokenHolder.address, + customRule.address, + customRuleWithCreditPayFunctionData, + tokenHolderSessionKeyData.nonce.toNumber(), + generateTokenHolderExecuteRuleCallPrefix(), + tokenHolderSessionPrivateKey, + ); + + const tokenHolderExecuteRuleFunctionData = tokenHolder.contract.methods.executeRule( + customRule.address, + customRuleWithCreditPayFunctionData, + tokenHolderSessionKeyData.nonce.toNumber(), + customRuleExTxSignature.r, + customRuleExTxSignature.s, + customRuleExTxSignature.v, + ).encodeABI(); + + const creditAmount = 5; + + const creditRuleExecuteRuleFunctionData = creditRule.contract.methods.executeRule( + creditAmount, + tokenHolder.address, + tokenHolderExecuteRuleFunctionData, + ).encodeABI(); + + const budgetHolderSessionKeyData = await budgetHolder.sessionKeys.call( + budgetHolderSessionPublicKey, + ); + + const { + exTxSignature: tokenHolderExecuteRuleExTxSignature, + } = await Utils.generateExTx( + budgetHolder.address, + creditRule.address, + creditRuleExecuteRuleFunctionData, + budgetHolderSessionKeyData.nonce.toNumber(), + generateTokenHolderExecuteRuleCallPrefix(), + budgetHolderSessionPrivateKey, + ); + + await budgetHolder.executeRule( + creditRule.address, + creditRuleExecuteRuleFunctionData, + budgetHolderSessionKeyData.nonce.toNumber(), + tokenHolderExecuteRuleExTxSignature.r, + tokenHolderExecuteRuleExTxSignature.s, + tokenHolderExecuteRuleExTxSignature.v, + ); + + await checkTransactions( + tokenRules, + budgetHolder.address, + creditAmount, + tokenHolder.address, + [beneficiaryAddress], + [amount], + ); + }); + }); +}); diff --git a/test/pricer_rule/pay.js b/test/pricer_rule/pay.js index 2d70c1e..96cd857 100644 --- a/test/pricer_rule/pay.js +++ b/test/pricer_rule/pay.js @@ -273,46 +273,40 @@ contract('PricerRule::pay', async () => { amount2BN, oraclePriceBN, new BN(conversionRate), new BN(conversionRateDecimals), ); - - const actualFromAddress = await tokenRules.recordedFrom.call(); - - const actualToAddress1 = await tokenRules.recordedTransfersTo.call(0); - const actualToAddress2 = await tokenRules.recordedTransfersTo.call(1); - const actualTransfersToLength = await tokenRules.recordedTransfersToLength.call(); - - const actualAmount1 = await tokenRules.recordedTransfersAmount.call(0); - const actualAmount2 = await tokenRules.recordedTransfersAmount.call(1); - const actualTransfersAmountLength = await tokenRules.recordedTransfersAmountLength.call(); + const transactionsLength = await tokenRules.transactionsLength.call(); + assert.isOk( + transactionsLength.eqn(1), + ); assert.strictEqual( - actualFromAddress, + await tokenRules.fromTransaction.call(0), fromAddress, ); - assert.isOk( - actualTransfersToLength.eqn(2), + const transfersToTransaction = await tokenRules.transfersToTransaction.call(0); + assert.strictEqual( + transfersToTransaction.length, + 2, ); - assert.strictEqual( - actualToAddress1, + transfersToTransaction[0], to1, ); - assert.strictEqual( - actualToAddress2, + transfersToTransaction[1], to2, ); - assert.isOk( - actualTransfersAmountLength.eqn(2), + const transfersAmountTransaction = await tokenRules.transfersAmountTransaction.call(0); + assert.strictEqual( + transfersAmountTransaction.length, + 2, ); - assert.isOk( - actualAmount1.eq(convertedAmount1BN), + (transfersAmountTransaction[0]).eq(convertedAmount1BN), ); - assert.isOk( - actualAmount2.eq(convertedAmount2BN), + (transfersAmountTransaction[1]).eq(convertedAmount2BN), ); }); }); diff --git a/test/pricer_rule/utils.js b/test/pricer_rule/utils.js index 48a4f0a..a9766c5 100644 --- a/test/pricer_rule/utils.js +++ b/test/pricer_rule/utils.js @@ -67,7 +67,7 @@ module.exports.createTokenEconomy = async (accountProvider) => { const token = await this.createEIP20Token(); - const tokenRules = await TokenRulesSpy.new(); + const tokenRules = await TokenRulesSpy.new(token.address); const baseCurrencyCode = 'OST'; diff --git a/test/token_holder/authorize_session.js b/test/token_holder/authorize_session.js index 3f23b0b..a99dc46 100644 --- a/test/token_holder/authorize_session.js +++ b/test/token_holder/authorize_session.js @@ -28,7 +28,9 @@ async function prepare( ) { const { utilityToken } = await TokenHolderUtils.createUtilityMockToken(); - const { tokenRules } = await TokenHolderUtils.createMockTokenRules(); + const { tokenRules } = await TokenHolderUtils.createMockTokenRules( + utilityToken.address, + ); const { tokenHolderOwnerAddress, diff --git a/test/token_holder/execute_redemption.js b/test/token_holder/execute_redemption.js index 92751a5..a844f53 100644 --- a/test/token_holder/execute_redemption.js +++ b/test/token_holder/execute_redemption.js @@ -100,7 +100,9 @@ async function prepare( ) { const { utilityToken } = await TokenHolderUtils.createUtilityMockToken(); - const { tokenRules } = await TokenHolderUtils.createMockTokenRules(); + const { tokenRules } = await TokenHolderUtils.createMockTokenRules( + utilityToken.address, + ); const { tokenHolderOwnerAddress, diff --git a/test/token_holder/execute_rule.js b/test/token_holder/execute_rule.js index 4afb6d9..b6e6897 100644 --- a/test/token_holder/execute_rule.js +++ b/test/token_holder/execute_rule.js @@ -279,7 +279,9 @@ async function prepare( ) { const { utilityToken } = await TokenHolderUtils.createUtilityMockToken(); - const { tokenRules } = await TokenHolderUtils.createMockTokenRules(); + const { tokenRules } = await TokenHolderUtils.createMockTokenRules( + utilityToken.address, + ); const { tokenHolderOwnerAddress, diff --git a/test/token_holder/logout.js b/test/token_holder/logout.js index 3dae08f..eb71bc2 100644 --- a/test/token_holder/logout.js +++ b/test/token_holder/logout.js @@ -23,7 +23,9 @@ async function prepare( ) { const { utilityToken } = await TokenHolderUtils.createUtilityMockToken(); - const { tokenRules } = await TokenHolderUtils.createMockTokenRules(); + const { tokenRules } = await TokenHolderUtils.createMockTokenRules( + utilityToken.address, + ); const authorizedSessionPublicKey = accountProvider.get(); diff --git a/test/token_holder/revoke_session.js b/test/token_holder/revoke_session.js index 8719801..89d3763 100644 --- a/test/token_holder/revoke_session.js +++ b/test/token_holder/revoke_session.js @@ -26,7 +26,9 @@ async function prepare( ) { const { utilityToken } = await TokenHolderUtils.createUtilityMockToken(); - const { tokenRules } = await TokenHolderUtils.createMockTokenRules(); + const { tokenRules } = await TokenHolderUtils.createMockTokenRules( + utilityToken.address, + ); const { tokenHolderOwnerAddress, diff --git a/test/token_holder/utils.js b/test/token_holder/utils.js index 3436ad9..6d12d61 100644 --- a/test/token_holder/utils.js +++ b/test/token_holder/utils.js @@ -27,8 +27,8 @@ class TokenHolderUtils { return { utilityToken }; } - static async createMockTokenRules() { - const tokenRules = await TokenRulesSpy.new(); + static async createMockTokenRules(tokenAddr) { + const tokenRules = await TokenRulesSpy.new(tokenAddr); return { tokenRules }; } diff --git a/test/transfer_rule/transfer_from.js b/test/transfer_rule/transfer_from.js index 68e08f3..c5692ba 100644 --- a/test/transfer_rule/transfer_from.js +++ b/test/transfer_rule/transfer_from.js @@ -30,7 +30,7 @@ contract('TransferRule::transferFrom', async (accounts) => { const expectedAmount = 10; const expectedTransfersAmountLength = 1; - const tokenRules = await TokenRulesSpy.new(); + const tokenRules = await TokenRulesSpy.new(accountProvider.get()); const transferRule = await TransferRule.new(tokenRules.address); @@ -38,34 +38,33 @@ contract('TransferRule::transferFrom', async (accounts) => { expectedFromAddress, expectedToAddress, expectedAmount, ); - const actualFromAddress = await tokenRules.recordedFrom.call(); - - const actualToAddress = await tokenRules.recordedTransfersTo.call(0); - const actualTransfersToLength = await tokenRules.recordedTransfersToLength.call(); - - const actualAmount = await tokenRules.recordedTransfersAmount.call(0); - const actualTransfersAmountLength = await tokenRules.recordedTransfersAmountLength.call(); + const transactionsLength = await tokenRules.transactionsLength.call(); + assert.isOk( + transactionsLength.eqn(1), + ); assert.strictEqual( - actualFromAddress, + await tokenRules.fromTransaction.call(0), expectedFromAddress, ); - assert.isOk( - actualTransfersToLength.eqn(expectedTransfersToLength), + const transfersToTransaction = await tokenRules.transfersToTransaction.call(0); + assert.strictEqual( + transfersToTransaction.length, + expectedTransfersToLength, ); - assert.strictEqual( - actualToAddress, + transfersToTransaction[0], expectedToAddress, ); - assert.isOk( - actualTransfersAmountLength.eqn(expectedTransfersAmountLength), + const transfersAmountTransaction = await tokenRules.transfersAmountTransaction.call(0); + assert.strictEqual( + transfersAmountTransaction.length, + expectedTransfersAmountLength, ); - assert.isOk( - actualAmount.eqn(expectedAmount), + (transfersAmountTransaction[0]).eqn(expectedAmount), ); }); });