From d6be6f79ce944258193d01fa3a23c5210e17ed49 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Thu, 26 Apr 2018 12:55:16 -0700 Subject: Add example whitelist contract and minimum tests --- .../contracts/current/test/Whitelist/Whitelist.sol | 40 +++++++--- packages/contracts/src/utils/types.ts | 1 + packages/contracts/test/exchange/transactions.ts | 91 +++++++++++++++++++++- 3 files changed, 121 insertions(+), 11 deletions(-) diff --git a/packages/contracts/src/contracts/current/test/Whitelist/Whitelist.sol b/packages/contracts/src/contracts/current/test/Whitelist/Whitelist.sol index 02158485e..6955b6d96 100644 --- a/packages/contracts/src/contracts/current/test/Whitelist/Whitelist.sol +++ b/packages/contracts/src/contracts/current/test/Whitelist/Whitelist.sol @@ -19,22 +19,24 @@ pragma solidity ^0.4.21; pragma experimental ABIEncoderV2; -import "../../protocol/Exchange/mixins/MTransactions.sol"; +import "../../protocol/Exchange/Exchange.sol"; import "../../protocol/Exchange/LibOrder.sol"; import "../../utils/Ownable/Ownable.sol"; contract Whitelist is Ownable { mapping (address => bool) public isWhitelisted; - MTransactions EXCHANGE; + Exchange EXCHANGE; - bytes txOriginSignatureType = new bytes(1); + bytes txOriginSignature = new bytes(1); + bytes4 fillOrderFunctionSelector; function Whitelist(address _exchange) public { - EXCHANGE = MTransactions(_exchange); - txOriginSignatureType[0] = 0x04; + EXCHANGE = Exchange(_exchange); + txOriginSignature[0] = 0x04; + fillOrderFunctionSelector = EXCHANGE.fillOrder.selector; } function updateWhitelistStatus(address target, bool isApproved) @@ -48,11 +50,31 @@ contract Whitelist is Ownable { LibOrder.Order memory order, uint256 takerAssetFillAmount, uint256 salt, - bytes memory signature) + bytes memory orderSignature) public { - require(isWhitelisted[msg.sender]); - bytes memory data = abi.encode(order, takerAssetFillAmount, signature); - EXCHANGE.executeTransaction(salt, msg.sender, data, txOriginSignatureType); + address takerAddress = msg.sender; + + // This contract must be the entry point for the transaction. + require(takerAddress == tx.origin); + + // Check if sender is on the whitelist. + require(isWhitelisted[takerAddress]); + + // Encode arguments into byte array. + bytes memory data = abi.encodeWithSelector( + fillOrderFunctionSelector, + order, + takerAssetFillAmount, + orderSignature + ); + + // Call `fillOrder`via `executeTransaction`. + EXCHANGE.executeTransaction( + salt, + takerAddress, + data, + txOriginSignature + ); } } \ No newline at end of file diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index cd2d128f1..1eeffc70e 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -111,6 +111,7 @@ export enum ContractName { DummyERC721Token = 'DummyERC721Token', TestLibBytes = 'TestLibBytes', Authorizable = 'Authorizable', + Whitelist = 'Whitelist', } export enum SignatureType { diff --git a/packages/contracts/test/exchange/transactions.ts b/packages/contracts/test/exchange/transactions.ts index 33fe11bfa..7242b0063 100644 --- a/packages/contracts/test/exchange/transactions.ts +++ b/packages/contracts/test/exchange/transactions.ts @@ -8,6 +8,7 @@ import * as Web3 from 'web3'; import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token'; import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy'; import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange'; +import { WhitelistContract } from '../../src/contract_wrappers/generated/whitelist'; import { artifacts } from '../../src/utils/artifacts'; import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; import { chaiSetup } from '../../src/utils/chai_setup'; @@ -55,6 +56,8 @@ describe('Exchange transactions', () => { let defaultMakerTokenAddress: string; let defaultTakerTokenAddress: string; + let makerPrivateKey: Buffer; + let takerPrivateKey: Buffer; before(async () => { await blockchainLifecycle.startAsync(); @@ -98,8 +101,8 @@ describe('Exchange transactions', () => { makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultMakerTokenAddress), takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultTakerTokenAddress), }; - const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; - const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)]; + makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; + takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)]; orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams); makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address); takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address); @@ -203,4 +206,88 @@ describe('Exchange transactions', () => { }); }); }); + + describe('Whitelist', () => { + let whitelist: WhitelistContract; + let whitelistOrderFactory: OrderFactory; + + before(async () => { + const whitelistInstance = await deployer.deployAsync(ContractName.Whitelist, [exchange.address]); + whitelist = new WhitelistContract(whitelistInstance.abi, whitelistInstance.address, provider); + const defaultOrderParams = { + ...constants.STATIC_ORDER_PARAMS, + senderAddress: whitelist.address, + exchangeAddress: exchange.address, + makerAddress, + feeRecipientAddress, + makerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultMakerTokenAddress), + takerAssetData: assetProxyUtils.encodeERC20ProxyData(defaultTakerTokenAddress), + }; + whitelistOrderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams); + }); + + beforeEach(async () => { + signedOrder = whitelistOrderFactory.newSignedOrder(); + erc20Balances = await erc20Wrapper.getBalancesAsync(); + }); + + it('should revert if taker has not been whitelisted', async () => { + const orderStruct = orderUtils.getOrderStruct(signedOrder); + const takerAssetFillAmount = signedOrder.takerAssetAmount; + const salt = ZeroEx.generatePseudoRandomSalt(); + return expect( + whitelist.fillOrderIfWhitelisted.sendTransactionAsync( + orderStruct, + takerAssetFillAmount, + salt, + signedOrder.signature, + { from: takerAddress }, + ), + ).to.be.rejectedWith(constants.REVERT); + }); + + it('should fill the order if taker has been whitelisted', async () => { + const isApproved = true; + await whitelist.updateWhitelistStatus.sendTransactionAsync(takerAddress, isApproved, { from: owner }); + + const orderStruct = orderUtils.getOrderStruct(signedOrder); + const takerAssetFillAmount = signedOrder.takerAssetAmount; + const salt = ZeroEx.generatePseudoRandomSalt(); + await whitelist.fillOrderIfWhitelisted.sendTransactionAsync( + orderStruct, + takerAssetFillAmount, + salt, + signedOrder.signature, + { from: takerAddress }, + ); + + const newBalances = await erc20Wrapper.getBalancesAsync(); + + const makerAssetFillAmount = signedOrder.makerAssetAmount; + const makerFeePaid = signedOrder.makerFee; + const takerFeePaid = signedOrder.takerFee; + + expect(newBalances[makerAddress][defaultMakerTokenAddress]).to.be.bignumber.equal( + erc20Balances[makerAddress][defaultMakerTokenAddress].minus(makerAssetFillAmount), + ); + expect(newBalances[makerAddress][defaultTakerTokenAddress]).to.be.bignumber.equal( + erc20Balances[makerAddress][defaultTakerTokenAddress].add(takerAssetFillAmount), + ); + expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[makerAddress][zrxToken.address].minus(makerFeePaid), + ); + expect(newBalances[takerAddress][defaultTakerTokenAddress]).to.be.bignumber.equal( + erc20Balances[takerAddress][defaultTakerTokenAddress].minus(takerAssetFillAmount), + ); + expect(newBalances[takerAddress][defaultMakerTokenAddress]).to.be.bignumber.equal( + erc20Balances[takerAddress][defaultMakerTokenAddress].add(makerAssetFillAmount), + ); + expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[takerAddress][zrxToken.address].minus(takerFeePaid), + ); + expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), + ); + }); + }); }); -- cgit