diff --git a/contracts/exchange/test/balance_stores/local_balance_store.ts b/contracts/exchange/test/balance_stores/local_balance_store.ts index 6f2a65806d..39f7692000 100644 --- a/contracts/exchange/test/balance_stores/local_balance_store.ts +++ b/contracts/exchange/test/balance_stores/local_balance_store.ts @@ -1,4 +1,4 @@ -import { constants } from '@0x/contracts-test-utils'; +import { constants, Numberish } from '@0x/contracts-test-utils'; import { assetDataUtils } from '@0x/order-utils'; import { AssetProxyId } from '@0x/types'; import { BigNumber } from '@0x/utils'; @@ -34,11 +34,24 @@ export class LocalBalanceStore extends BalanceStore { /** * Decreases the ETH balance of an address to simulate gas usage. + * @param senderAddress Address whose ETH balance to decrease. + * @param amount Amount to decrease the balance by. */ - public burnGas(senderAddress: string, amount: BigNumber | number): void { + public burnGas(senderAddress: string, amount: Numberish): void { this._balances.eth[senderAddress] = this._balances.eth[senderAddress].minus(amount); } + /** + * Sends ETH from `fromAddress` to `toAddress`. + * @param fromAddress Sender of ETH. + * @param toAddress Receiver of ETH. + * @param amount Amount of ETH to transfer. + */ + public sendEth(fromAddress: string, toAddress: string, amount: Numberish): void { + this._balances.eth[fromAddress] = this._balances.eth[fromAddress].minus(amount); + this._balances.eth[toAddress] = this._balances.eth[toAddress].plus(amount); + } + /** * Transfers assets from `fromAddress` to `toAddress`. * @param fromAddress Sender of asset(s) diff --git a/contracts/exchange/test/balance_stores/types.ts b/contracts/exchange/test/balance_stores/types.ts index c8090f0e39..75789b614b 100644 --- a/contracts/exchange/test/balance_stores/types.ts +++ b/contracts/exchange/test/balance_stores/types.ts @@ -1,5 +1,5 @@ import { ERC1155MintableContract } from '@0x/contracts-erc1155'; -import { DummyERC20TokenContract, DummyNoReturnERC20TokenContract } from '@0x/contracts-erc20'; +import { DummyERC20TokenContract, DummyNoReturnERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20'; import { DummyERC721TokenContract } from '@0x/contracts-erc721'; import { BigNumber } from '@0x/utils'; @@ -15,7 +15,7 @@ interface TokenData { export type TokenAddresses = TokenData; export type TokenContracts = TokenData< - Array, + Array, DummyERC721TokenContract[], ERC1155MintableContract[] >; @@ -29,7 +29,7 @@ export type TokenOwnersByName = Named
; export type TokenAddressesByName = TokenData, Named
, Named
>; export type TokenContractsByName = TokenData< - Named, + Named, Named, Named >; diff --git a/contracts/integrations/test/actors/base.ts b/contracts/integrations/test/actors/base.ts index 65e19f6826..3c3abb953c 100644 --- a/contracts/integrations/test/actors/base.ts +++ b/contracts/integrations/test/actors/base.ts @@ -1,5 +1,6 @@ import { DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20'; -import { constants } from '@0x/contracts-test-utils'; +import { constants, TransactionFactory } from '@0x/contracts-test-utils'; +import { SignatureType, SignedZeroExTransaction, ZeroExTransaction } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { DeploymentManager } from '../utils/deployment_manager'; @@ -7,21 +8,30 @@ import { DeploymentManager } from '../utils/deployment_manager'; export type Constructor = new (...args: any[]) => T; export interface ActorConfig { - address: string; name?: string; deployment: DeploymentManager; [mixinProperty: string]: any; } export class Actor { + public static count: number = 0; public readonly address: string; public readonly name: string; + public readonly privateKey: Buffer; public readonly deployment: DeploymentManager; + protected readonly transactionFactory: TransactionFactory; constructor(config: ActorConfig) { - this.address = config.address; - this.name = config.name || config.address; + Actor.count++; + this.address = config.deployment.accounts[Actor.count]; + this.name = config.name || this.address; this.deployment = config.deployment; + this.privateKey = constants.TESTRPC_PRIVATE_KEYS[config.deployment.accounts.indexOf(this.address)]; + this.transactionFactory = new TransactionFactory( + this.privateKey, + config.deployment.exchange.address, + config.deployment.chainId, + ); } /** @@ -51,4 +61,14 @@ export class Actor { { from: this.address }, ); } + + /** + * Signs a transaction. + */ + public async signTransactionAsync( + customTransactionParams: Partial, + signatureType: SignatureType = SignatureType.EthSign, + ): Promise { + return this.transactionFactory.newSignedTransactionAsync(customTransactionParams, signatureType); + } } diff --git a/contracts/integrations/test/actors/fee_recipient.ts b/contracts/integrations/test/actors/fee_recipient.ts new file mode 100644 index 0000000000..a560a81816 --- /dev/null +++ b/contracts/integrations/test/actors/fee_recipient.ts @@ -0,0 +1,47 @@ +import { BaseContract } from '@0x/base-contract'; +import { ApprovalFactory, SignedCoordinatorApproval } from '@0x/contracts-coordinator'; +import { SignatureType, SignedZeroExTransaction } from '@0x/types'; + +import { Actor, ActorConfig, Constructor } from './base'; + +export interface FeeRecipientConfig extends ActorConfig { + verifyingContract?: BaseContract; +} + +export function FeeRecipientMixin(Base: TBase) { + return class extends Base { + public readonly actor: Actor; + public readonly approvalFactory?: ApprovalFactory; + + /** + * The mixin pattern requires that this constructor uses `...args: any[]`, but this class + * really expects a single `FeeRecipientConfig` parameter (assuming `Actor` is used as the + * base class). + */ + constructor(...args: any[]) { + super(...args); + this.actor = (this as any) as Actor; + + const { verifyingContract } = args[0] as FeeRecipientConfig; + if (verifyingContract !== undefined) { + this.approvalFactory = new ApprovalFactory(this.actor.privateKey, verifyingContract.address); + } + } + + /** + * Signs an coordinator transaction. + */ + public signCoordinatorApproval( + transaction: SignedZeroExTransaction, + txOrigin: string, + signatureType: SignatureType = SignatureType.EthSign, + ): SignedCoordinatorApproval { + if (this.approvalFactory === undefined) { + throw new Error('No verifying contract provided in FeeRecipient constructor'); + } + return this.approvalFactory.newSignedApproval(transaction, txOrigin, signatureType); + } + }; +} + +export class FeeRecipient extends FeeRecipientMixin(Actor) {} diff --git a/contracts/integrations/test/actors/index.ts b/contracts/integrations/test/actors/index.ts index ab525a5a70..14934ac89a 100644 --- a/contracts/integrations/test/actors/index.ts +++ b/contracts/integrations/test/actors/index.ts @@ -1,3 +1,6 @@ +export { Actor } from './base'; export { Maker } from './maker'; export { PoolOperator } from './pool_operator'; +export { FeeRecipient } from './fee_recipient'; export * from './hybrids'; +export * from './utils'; diff --git a/contracts/integrations/test/actors/maker.ts b/contracts/integrations/test/actors/maker.ts index 870716c7d2..fc92f4a5fa 100644 --- a/contracts/integrations/test/actors/maker.ts +++ b/contracts/integrations/test/actors/maker.ts @@ -31,9 +31,7 @@ export function MakerMixin(Base: TBase) { chainId: this.actor.deployment.chainId, ...orderConfig, }; - const privateKey = - constants.TESTRPC_PRIVATE_KEYS[this.actor.deployment.accounts.indexOf(this.actor.address)]; - this.orderFactory = new OrderFactory(privateKey, defaultOrderParams); + this.orderFactory = new OrderFactory(this.actor.privateKey, defaultOrderParams); } /** diff --git a/contracts/integrations/test/actors/utils.ts b/contracts/integrations/test/actors/utils.ts new file mode 100644 index 0000000000..0bbc6e07c8 --- /dev/null +++ b/contracts/integrations/test/actors/utils.ts @@ -0,0 +1,8 @@ +import { TokenOwnersByName } from '@0x/contracts-exchange'; +import * as _ from 'lodash'; + +import { Actor } from './base'; + +export function actorAddressesByName(actors: Actor[]): TokenOwnersByName { + return _.zipObject(actors.map(actor => actor.name), actors.map(actor => actor.address)); +} diff --git a/contracts/integrations/test/coordinator/coordinator.ts b/contracts/integrations/test/coordinator/coordinator.ts index c4671a7994..161fd2fe30 100644 --- a/contracts/integrations/test/coordinator/coordinator.ts +++ b/contracts/integrations/test/coordinator/coordinator.ts @@ -1,419 +1,457 @@ -import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy'; -import { ApprovalFactory, artifacts, CoordinatorContract } from '@0x/contracts-coordinator'; -import { artifacts as erc20Artifacts, DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20'; +import { CoordinatorContract, SignedCoordinatorApproval } from '@0x/contracts-coordinator'; import { - artifacts as exchangeArtifacts, + BlockchainBalanceStore, + LocalBalanceStore, constants as exchangeConstants, - ExchangeContract, + ExchangeCancelEventArgs, + ExchangeCancelUpToEventArgs, exchangeDataEncoder, + ExchangeEvents, + ExchangeFillEventArgs, ExchangeFunctionName, - TestProtocolFeeCollectorContract, } from '@0x/contracts-exchange'; -import { - blockchainTests, - constants, - hexConcat, - hexSlice, - OrderFactory, - TransactionFactory, -} from '@0x/contracts-test-utils'; -import { assetDataUtils, CoordinatorRevertErrors, transactionHashUtils } from '@0x/order-utils'; +import { blockchainTests, expect, hexConcat, hexSlice, verifyEvents } from '@0x/contracts-test-utils'; +import { assetDataUtils, CoordinatorRevertErrors, orderHashUtils, transactionHashUtils } from '@0x/order-utils'; +import { SignedOrder, SignedZeroExTransaction } from '@0x/types'; import { BigNumber } from '@0x/utils'; +import { TransactionReceiptWithDecodedLogs } from 'ethereum-types'; -import { CoordinatorTestFactory } from './coordinator_test_factory'; +import { Actor, actorAddressesByName, FeeRecipient, Maker } from '../actors'; +import { deployCoordinatorAsync } from './deploy_coordinator'; +import { DeploymentManager } from '../utils/deployment_manager'; // tslint:disable:no-unnecessary-type-assertion blockchainTests.resets('Coordinator tests', env => { - let chainId: number; - let makerAddress: string; - let owner: string; - let takerAddress: string; - let feeRecipientAddress: string; + let deployment: DeploymentManager; + let coordinator: CoordinatorContract; + let balanceStore: BlockchainBalanceStore; - let erc20Proxy: ERC20ProxyContract; - let erc20TokenA: DummyERC20TokenContract; - let erc20TokenB: DummyERC20TokenContract; - let makerFeeToken: DummyERC20TokenContract; - let takerFeeToken: DummyERC20TokenContract; - let coordinatorContract: CoordinatorContract; - let exchange: ExchangeContract; - let protocolFeeCollector: TestProtocolFeeCollectorContract; - let wethContract: WETH9Contract; - - let erc20Wrapper: ERC20Wrapper; - let orderFactory: OrderFactory; - let takerTransactionFactory: TransactionFactory; - let makerTransactionFactory: TransactionFactory; - let approvalFactory: ApprovalFactory; - let testFactory: CoordinatorTestFactory; - - const GAS_PRICE = new BigNumber(env.txDefaults.gasPrice || constants.DEFAULT_GAS_PRICE); - const PROTOCOL_FEE_MULTIPLIER = new BigNumber(150000); - const PROTOCOL_FEE = GAS_PRICE.times(PROTOCOL_FEE_MULTIPLIER); + let maker: Maker; + let taker: Actor; + let feeRecipient: FeeRecipient; before(async () => { - chainId = await env.getChainIdAsync(); - const accounts = await env.getAccountAddressesAsync(); - const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts); + deployment = await DeploymentManager.deployAsync(env, { + numErc20TokensToDeploy: 4, + numErc721TokensToDeploy: 0, + numErc1155TokensToDeploy: 0, + }); + coordinator = await deployCoordinatorAsync(deployment, env); - // Deploy Exchange - exchange = await ExchangeContract.deployFrom0xArtifactAsync( - exchangeArtifacts.Exchange, - env.provider, - env.txDefaults, + const [makerToken, takerToken, makerFeeToken, takerFeeToken] = deployment.tokens.erc20; + + taker = new Actor({ name: 'Taker', deployment }); + feeRecipient = new FeeRecipient({ + name: 'Fee recipient', + deployment, + verifyingContract: coordinator, + }); + maker = new Maker({ + name: 'Maker', + deployment, + orderConfig: { + senderAddress: coordinator.address, + feeRecipientAddress: feeRecipient.address, + makerAssetData: assetDataUtils.encodeERC20AssetData(makerToken.address), + takerAssetData: assetDataUtils.encodeERC20AssetData(takerToken.address), + makerFeeAssetData: assetDataUtils.encodeERC20AssetData(makerFeeToken.address), + takerFeeAssetData: assetDataUtils.encodeERC20AssetData(takerFeeToken.address), + }, + }); + + taker.configureERC20TokenAsync(takerToken); + taker.configureERC20TokenAsync(takerFeeToken); + taker.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address); + maker.configureERC20TokenAsync(makerToken); + maker.configureERC20TokenAsync(makerFeeToken); + + balanceStore = new BlockchainBalanceStore( + { + ...actorAddressesByName([maker, taker, feeRecipient]), + Coordinator: coordinator.address, + StakingProxy: deployment.staking.stakingProxy.address, + }, + { erc20: { makerToken, takerToken, makerFeeToken, takerFeeToken, wETH: deployment.tokens.weth } }, {}, - new BigNumber(chainId), - ); - - // Set up ERC20 - erc20Wrapper = new ERC20Wrapper(env.provider, usedAddresses, owner); - erc20Proxy = await erc20Wrapper.deployProxyAsync(); - const numDummyErc20ToDeploy = 4; - [erc20TokenA, erc20TokenB, makerFeeToken, takerFeeToken] = await erc20Wrapper.deployDummyTokensAsync( - numDummyErc20ToDeploy, - constants.DUMMY_TOKEN_DECIMALS, - ); - await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(exchange.address, { from: owner }); - await exchange.registerAssetProxy.awaitTransactionSuccessAsync(erc20Proxy.address, { from: owner }); - - // Set up WETH - wethContract = await WETH9Contract.deployFrom0xArtifactAsync( - erc20Artifacts.WETH9, - env.provider, - env.txDefaults, - {}, - ); - const weth = new DummyERC20TokenContract(wethContract.address, env.provider); - erc20Wrapper.addDummyTokenContract(weth); - await erc20Wrapper.setBalancesAndAllowancesAsync(); - - // Set up Protocol Fee Collector - protocolFeeCollector = await TestProtocolFeeCollectorContract.deployFrom0xArtifactAsync( - exchangeArtifacts.TestProtocolFeeCollector, - env.provider, - env.txDefaults, - {}, - weth.address, - ); - await exchange.setProtocolFeeMultiplier.awaitTransactionSuccessAsync(PROTOCOL_FEE_MULTIPLIER); - await exchange.setProtocolFeeCollectorAddress.awaitTransactionSuccessAsync(protocolFeeCollector.address); - for (const account of usedAddresses) { - await wethContract.deposit.awaitTransactionSuccessAsync({ - from: account, - value: constants.ONE_ETHER, - }); - await wethContract.approve.awaitTransactionSuccessAsync( - protocolFeeCollector.address, - constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, - { - from: account, - }, - ); - } - erc20Wrapper.addTokenOwnerAddress(protocolFeeCollector.address); - - // Deploy Coordinator - coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync( - artifacts.Coordinator, - env.provider, - env.txDefaults, - { ...exchangeArtifacts, ...artifacts }, - exchange.address, - new BigNumber(chainId), - ); - erc20Wrapper.addTokenOwnerAddress(coordinatorContract.address); - - // Configure order defaults - const defaultOrderParams = { - ...constants.STATIC_ORDER_PARAMS, - senderAddress: coordinatorContract.address, - makerAddress, - feeRecipientAddress, - makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address), - takerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenB.address), - makerFeeAssetData: assetDataUtils.encodeERC20AssetData(makerFeeToken.address), - takerFeeAssetData: assetDataUtils.encodeERC20AssetData(takerFeeToken.address), - exchangeAddress: exchange.address, - chainId, - }; - const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; - const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)]; - const feeRecipientPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(feeRecipientAddress)]; - orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams); - makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address, chainId); - takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address, chainId); - approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, coordinatorContract.address); - testFactory = new CoordinatorTestFactory( - coordinatorContract, - erc20Wrapper, - makerAddress, - takerAddress, - feeRecipientAddress, - protocolFeeCollector.address, - erc20TokenA.address, - erc20TokenB.address, - makerFeeToken.address, - takerFeeToken.address, - weth.address, - GAS_PRICE, - PROTOCOL_FEE_MULTIPLIER, ); }); - describe('single order fills', () => { - for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) { - it(`${fnName} should fill the order with a signed approval`, async () => { - const order = await orderFactory.newSignedOrderAsync(); - const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]); - const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); - const approval = approvalFactory.newSignedApproval(transaction, takerAddress); - const txData = { from: takerAddress, value: PROTOCOL_FEE }; - await testFactory.executeFillTransactionTestAsync( - [order], - transaction, - takerAddress, - [approval.signature], - txData, + function simulateFills( + orders: SignedOrder[], + txReceipt: TransactionReceiptWithDecodedLogs, + msgValue: BigNumber = new BigNumber(0), + ): LocalBalanceStore { + const localBalanceStore = LocalBalanceStore.create(balanceStore); + // Transaction gas cost + localBalanceStore.burnGas(txReceipt.from, DeploymentManager.gasPrice.times(txReceipt.gasUsed)); + + for (const order of orders) { + // Taker -> Maker + localBalanceStore.transferAsset(taker.address, maker.address, order.takerAssetAmount, order.takerAssetData); + // Maker -> Taker + localBalanceStore.transferAsset(maker.address, taker.address, order.makerAssetAmount, order.makerAssetData); + // Taker -> Fee Recipient + localBalanceStore.transferAsset( + taker.address, + feeRecipient.address, + order.takerFee, + order.takerFeeAssetData, + ); + // Maker -> Fee Recipient + localBalanceStore.transferAsset( + maker.address, + feeRecipient.address, + order.makerFee, + order.makerFeeAssetData, + ); + + // Protocol fee + if (msgValue.isGreaterThanOrEqualTo(DeploymentManager.protocolFee)) { + localBalanceStore.sendEth( + txReceipt.from, + deployment.staking.stakingProxy.address, + DeploymentManager.protocolFee, ); + msgValue = msgValue.minus(DeploymentManager.protocolFee); + } else { + localBalanceStore.transferAsset( + taker.address, + deployment.staking.stakingProxy.address, + DeploymentManager.protocolFee, + assetDataUtils.encodeERC20AssetData(deployment.tokens.weth.address), + ); + } + } + + return localBalanceStore; + } + + function expectedFillEvent(order: SignedOrder): ExchangeFillEventArgs { + return { + makerAddress: order.makerAddress, + takerAddress: taker.address, + senderAddress: order.senderAddress, + feeRecipientAddress: order.feeRecipientAddress, + makerAssetData: order.makerAssetData, + takerAssetData: order.takerAssetData, + makerFeeAssetData: order.makerFeeAssetData, + takerFeeAssetData: order.takerFeeAssetData, + makerAssetFilledAmount: order.makerAssetAmount, + takerAssetFilledAmount: order.takerAssetAmount, + makerFeePaid: order.makerFee, + takerFeePaid: order.takerFee, + protocolFeePaid: DeploymentManager.protocolFee, + orderHash: orderHashUtils.getOrderHashHex(order), + }; + } + + describe('single order fills', () => { + let order: SignedOrder; + let data: string; + let transaction: SignedZeroExTransaction; + let approval: SignedCoordinatorApproval; + + for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) { + before(async () => { + order = await maker.signOrderAsync(); + data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]); + transaction = await taker.signTransactionAsync({ + data, + gasPrice: DeploymentManager.gasPrice, + }); + approval = feeRecipient.signCoordinatorApproval(transaction, taker.address); + }); + + it(`${fnName} should fill the order with a signed approval`, async () => { + await balanceStore.updateBalancesAsync(); + const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync( + transaction, + taker.address, + transaction.signature, + [approval.signature], + { from: taker.address, value: DeploymentManager.protocolFee }, + ); + + const expectedBalances = simulateFills([order], txReceipt, DeploymentManager.protocolFee); + await balanceStore.updateBalancesAsync(); + balanceStore.assertEquals(expectedBalances); + verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill); }); it(`${fnName} should fill the order if called by approver (eth protocol fee, no refund)`, async () => { - const order = await orderFactory.newSignedOrderAsync(); - const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]); - const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); - const txData = { from: feeRecipientAddress, value: PROTOCOL_FEE }; - await testFactory.executeFillTransactionTestAsync( - [order], + await balanceStore.updateBalancesAsync(); + const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync( transaction, - feeRecipientAddress, + feeRecipient.address, + transaction.signature, [], - txData, + { from: feeRecipient.address, value: DeploymentManager.protocolFee }, ); + + const expectedBalances = simulateFills([order], txReceipt, DeploymentManager.protocolFee); + await balanceStore.updateBalancesAsync(); + balanceStore.assertEquals(expectedBalances); + verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill); }); it(`${fnName} should fill the order if called by approver (eth protocol fee, refund)`, async () => { - const order = await orderFactory.newSignedOrderAsync(); - const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]); - const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); - const txData = { from: feeRecipientAddress, value: PROTOCOL_FEE.plus(1) }; - await testFactory.executeFillTransactionTestAsync( - [order], + await balanceStore.updateBalancesAsync(); + const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync( transaction, - feeRecipientAddress, + feeRecipient.address, + transaction.signature, [], - txData, + { from: feeRecipient.address, value: DeploymentManager.protocolFee.plus(1) }, ); + + const expectedBalances = simulateFills([order], txReceipt, DeploymentManager.protocolFee.plus(1)); + await balanceStore.updateBalancesAsync(); + balanceStore.assertEquals(expectedBalances); + verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill); }); it(`${fnName} should fill the order if called by approver (weth protocol fee, no refund)`, async () => { - const order = await orderFactory.newSignedOrderAsync(); - const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]); - const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); - const txData = { from: feeRecipientAddress }; - await testFactory.executeFillTransactionTestAsync( - [order], + await balanceStore.updateBalancesAsync(); + const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync( transaction, - feeRecipientAddress, + feeRecipient.address, + transaction.signature, [], - txData, + { from: feeRecipient.address }, ); + + const expectedBalances = simulateFills([order], txReceipt); + await balanceStore.updateBalancesAsync(); + balanceStore.assertEquals(expectedBalances); + verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill); }); it(`${fnName} should fill the order if called by approver (weth protocol fee, refund)`, async () => { - const order = await orderFactory.newSignedOrderAsync(); - const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]); - const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); - const txData = { from: feeRecipientAddress, value: new BigNumber(1) }; - await testFactory.executeFillTransactionTestAsync( - [order], + await balanceStore.updateBalancesAsync(); + const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync( transaction, - feeRecipientAddress, + feeRecipient.address, + transaction.signature, [], - txData, - ); - }); - it(`${fnName} should fill the order if called by approver`, async () => { - const order = await orderFactory.newSignedOrderAsync(); - const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]); - const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); - const txData = { from: feeRecipientAddress, value: PROTOCOL_FEE }; - await testFactory.executeFillTransactionTestAsync( - [order], - transaction, - feeRecipientAddress, - [], - txData, + { from: feeRecipient.address, value: new BigNumber(1) }, ); + + const expectedBalances = simulateFills([order], txReceipt, new BigNumber(1)); + await balanceStore.updateBalancesAsync(); + balanceStore.assertEquals(expectedBalances); + verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill); }); it(`${fnName} should revert with no approval signature`, async () => { - const orders = [await orderFactory.newSignedOrderAsync()]; - const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); - const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); const transactionHash = transactionHashUtils.getTransactionHashHex(transaction); - await testFactory.executeFillTransactionTestAsync( - orders, + const tx = coordinator.executeTransaction.awaitTransactionSuccessAsync( transaction, - takerAddress, + taker.address, + transaction.signature, [], - { - from: takerAddress, - gas: constants.MAX_EXECUTE_TRANSACTION_GAS, - value: PROTOCOL_FEE, - }, - new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, feeRecipientAddress), + { from: taker.address, value: DeploymentManager.protocolFee }, ); + + const expectedError = new CoordinatorRevertErrors.InvalidApprovalSignatureError( + transactionHash, + feeRecipient.address, + ); + return expect(tx).to.revertWith(expectedError); }); it(`${fnName} should revert with an invalid approval signature`, async () => { - const orders = [await orderFactory.newSignedOrderAsync()]; - const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); - const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); - const approval = approvalFactory.newSignedApproval(transaction, takerAddress); - const signature = hexConcat( + const approvalSignature = hexConcat( hexSlice(approval.signature, 0, 2), '0xFFFFFFFF', hexSlice(approval.signature, 6), ); const transactionHash = transactionHashUtils.getTransactionHashHex(transaction); - await testFactory.executeFillTransactionTestAsync( - orders, + const tx = coordinator.executeTransaction.awaitTransactionSuccessAsync( transaction, - takerAddress, - [signature], - { from: takerAddress, value: PROTOCOL_FEE }, - new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, feeRecipientAddress), + taker.address, + transaction.signature, + [approvalSignature], + { from: taker.address, value: DeploymentManager.protocolFee }, ); + + const expectedError = new CoordinatorRevertErrors.InvalidApprovalSignatureError( + transactionHash, + feeRecipient.address, + ); + return expect(tx).to.revertWith(expectedError); }); it(`${fnName} should revert if not called by tx signer or approver`, async () => { - const orders = [await orderFactory.newSignedOrderAsync()]; - const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); - const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); - const approval = approvalFactory.newSignedApproval(transaction, takerAddress); - await testFactory.executeFillTransactionTestAsync( - orders, + const tx = coordinator.executeTransaction.awaitTransactionSuccessAsync( transaction, - takerAddress, + taker.address, + transaction.signature, [approval.signature], - { from: owner, value: PROTOCOL_FEE }, - new CoordinatorRevertErrors.InvalidOriginError(takerAddress), + { from: maker.address, value: DeploymentManager.protocolFee }, ); + + const expectedError = new CoordinatorRevertErrors.InvalidOriginError(taker.address); + return expect(tx).to.revertWith(expectedError); }); } }); describe('batch order fills', () => { + let orders: SignedOrder[]; + let data: string; + let transaction: SignedZeroExTransaction; + let approval: SignedCoordinatorApproval; + for (const fnName of [...exchangeConstants.MARKET_FILL_FN_NAMES, ...exchangeConstants.BATCH_FILL_FN_NAMES]) { + before(async () => { + orders = [await maker.signOrderAsync(), await maker.signOrderAsync()]; + data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); + transaction = await taker.signTransactionAsync({ + data, + gasPrice: DeploymentManager.gasPrice, + }); + approval = feeRecipient.signCoordinatorApproval(transaction, taker.address); + }); + it(`${fnName} should fill the orders with a signed approval`, async () => { - const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; - const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); - const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); - const approval = approvalFactory.newSignedApproval(transaction, takerAddress); - await testFactory.executeFillTransactionTestAsync( - orders, + await balanceStore.updateBalancesAsync(); + const value = DeploymentManager.protocolFee.times(orders.length); + const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync( transaction, - takerAddress, + taker.address, + transaction.signature, [approval.signature], - { - from: takerAddress, - gas: constants.MAX_EXECUTE_TRANSACTION_GAS, - value: PROTOCOL_FEE.times(orders.length), - }, + { from: taker.address, value }, ); + + const expectedBalances = simulateFills(orders, txReceipt, value); + await balanceStore.updateBalancesAsync(); + balanceStore.assertEquals(expectedBalances); + verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill); }); it(`${fnName} should fill the orders if called by approver (eth fee, no refund)`, async () => { - const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; - const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); - const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); - await testFactory.executeFillTransactionTestAsync(orders, transaction, feeRecipientAddress, [], { - from: feeRecipientAddress, - gas: constants.MAX_EXECUTE_TRANSACTION_GAS, - value: PROTOCOL_FEE.times(orders.length), - }); + await balanceStore.updateBalancesAsync(); + const value = DeploymentManager.protocolFee.times(orders.length); + const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync( + transaction, + feeRecipient.address, + transaction.signature, + [], + { from: feeRecipient.address, value }, + ); + + const expectedBalances = simulateFills(orders, txReceipt, value); + await balanceStore.updateBalancesAsync(); + balanceStore.assertEquals(expectedBalances); + verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill); }); it(`${fnName} should fill the orders if called by approver (mixed fees, refund)`, async () => { - const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; - const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); - const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); - await testFactory.executeFillTransactionTestAsync(orders, transaction, feeRecipientAddress, [], { - from: feeRecipientAddress, - gas: constants.MAX_EXECUTE_TRANSACTION_GAS, - value: PROTOCOL_FEE.times(orders.length).plus(1), - }); + await balanceStore.updateBalancesAsync(); + const value = DeploymentManager.protocolFee.plus(1); + const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync( + transaction, + feeRecipient.address, + transaction.signature, + [], + { from: feeRecipient.address, value }, + ); + + const expectedBalances = simulateFills(orders, txReceipt, value); + await balanceStore.updateBalancesAsync(); + balanceStore.assertEquals(expectedBalances); + verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill); }); it(`${fnName} should revert with an invalid approval signature`, async () => { - const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; - const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); - const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); - const approval = approvalFactory.newSignedApproval(transaction, takerAddress); - const signature = hexConcat( + const approvalSignature = hexConcat( hexSlice(approval.signature, 0, 2), '0xFFFFFFFF', hexSlice(approval.signature, 6), ); const transactionHash = transactionHashUtils.getTransactionHashHex(transaction); - await testFactory.executeFillTransactionTestAsync( - orders, + const tx = coordinator.executeTransaction.awaitTransactionSuccessAsync( transaction, - takerAddress, - [signature], - { from: takerAddress, value: PROTOCOL_FEE.times(orders.length) }, - new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, feeRecipientAddress), + taker.address, + transaction.signature, + [approvalSignature], + { from: taker.address, value: DeploymentManager.protocolFee.times(orders.length) }, ); + const expectedError = new CoordinatorRevertErrors.InvalidApprovalSignatureError( + transactionHash, + feeRecipient.address, + ); + return expect(tx).to.revertWith(expectedError); }); it(`${fnName} should revert if not called by tx signer or approver`, async () => { - const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; - const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); - const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); - const approval = approvalFactory.newSignedApproval(transaction, takerAddress); - await testFactory.executeFillTransactionTestAsync( - orders, + const tx = coordinator.executeTransaction.awaitTransactionSuccessAsync( transaction, - takerAddress, + taker.address, + transaction.signature, [approval.signature], - { from: owner, value: PROTOCOL_FEE.times(orders.length) }, - new CoordinatorRevertErrors.InvalidOriginError(takerAddress), + { from: maker.address, value: DeploymentManager.protocolFee.times(orders.length) }, ); + const expectedError = new CoordinatorRevertErrors.InvalidOriginError(taker.address); + return expect(tx).to.revertWith(expectedError); }); } }); describe('cancels', () => { + function expectedCancelEvent(order: SignedOrder): ExchangeCancelEventArgs { + return { + makerAddress: order.makerAddress, + senderAddress: order.senderAddress, + feeRecipientAddress: order.feeRecipientAddress, + makerAssetData: order.makerAssetData, + takerAssetData: order.takerAssetData, + orderHash: orderHashUtils.getOrderHashHex(order), + }; + } + it('cancelOrder call should be successful without an approval', async () => { - const orders = [await orderFactory.newSignedOrderAsync()]; - const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, orders); - const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data }); - await testFactory.executeCancelTransactionTestAsync( - ExchangeFunctionName.CancelOrder, - orders, + const order = await maker.signOrderAsync(); + const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [order]); + const transaction = await maker.signTransactionAsync({ + data, + gasPrice: DeploymentManager.gasPrice, + }); + const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync( transaction, - makerAddress, + maker.address, + transaction.signature, [], - { - from: makerAddress, - }, + { from: maker.address }, ); + + verifyEvents(txReceipt, [expectedCancelEvent(order)], ExchangeEvents.Cancel); }); it('batchCancelOrders call should be successful without an approval', async () => { - const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; + const orders = [await maker.signOrderAsync(), await maker.signOrderAsync()]; const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.BatchCancelOrders, orders); - const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data }); - await testFactory.executeCancelTransactionTestAsync( - ExchangeFunctionName.BatchCancelOrders, - orders, + const transaction = await maker.signTransactionAsync({ + data, + gasPrice: DeploymentManager.gasPrice, + }); + const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync( transaction, - makerAddress, + maker.address, + transaction.signature, [], - { - from: makerAddress, - }, + { from: maker.address }, ); + + verifyEvents(txReceipt, orders.map(order => expectedCancelEvent(order)), ExchangeEvents.Cancel); }); it('cancelOrdersUpTo call should be successful without an approval', async () => { const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrdersUpTo, []); - const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data }); - await testFactory.executeCancelTransactionTestAsync( - ExchangeFunctionName.CancelOrdersUpTo, - [], + const transaction = await maker.signTransactionAsync({ + data, + gasPrice: DeploymentManager.gasPrice, + }); + const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync( transaction, - makerAddress, + maker.address, + transaction.signature, [], - { - from: makerAddress, - }, + { from: maker.address }, ); + + const expectedEvent: ExchangeCancelUpToEventArgs = { + makerAddress: maker.address, + orderSenderAddress: coordinator.address, + orderEpoch: new BigNumber(1), + }; + verifyEvents(txReceipt, [expectedEvent], ExchangeEvents.CancelUpTo); }); }); }); diff --git a/contracts/integrations/test/coordinator/coordinator_test_factory.ts b/contracts/integrations/test/coordinator/coordinator_test_factory.ts deleted file mode 100644 index bd3ba81f2d..0000000000 --- a/contracts/integrations/test/coordinator/coordinator_test_factory.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { ERC20Wrapper } from '@0x/contracts-asset-proxy'; -import { CoordinatorContract } from '@0x/contracts-coordinator'; -import { - ExchangeCancelEventArgs, - ExchangeCancelUpToEventArgs, - ExchangeEvents, - ExchangeFillEventArgs, - ExchangeFunctionName, -} from '@0x/contracts-exchange'; -import { expect, Numberish, TokenBalances, verifyEvents, web3Wrapper } from '@0x/contracts-test-utils'; -import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; -import { SignedOrder, SignedZeroExTransaction } from '@0x/types'; -import { BigNumber, RevertError } from '@0x/utils'; -import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types'; -import * as _ from 'lodash'; - -export class CoordinatorTestFactory { - private readonly _addresses: string[]; - private readonly _protocolFee: BigNumber; - - private static _expectedCancelEvent(order: SignedOrder): ExchangeCancelEventArgs { - return { - makerAddress: order.makerAddress, - senderAddress: order.senderAddress, - feeRecipientAddress: order.feeRecipientAddress, - makerAssetData: order.makerAssetData, - takerAssetData: order.takerAssetData, - orderHash: orderHashUtils.getOrderHashHex(order), - }; - } - - constructor( - private readonly _coordinatorContract: CoordinatorContract, - private readonly _erc20Wrapper: ERC20Wrapper, - private readonly _makerAddress: string, - private readonly _takerAddress: string, - private readonly _feeRecipientAddress: string, - private readonly _protocolFeeCollectorAddress: string, - private readonly _makerAssetAddress: string, - private readonly _takerAssetAddress: string, - private readonly _makerFeeAssetAddress: string, - private readonly _takerFeeAssetAddress: string, - private readonly _wethAddress: string, - private readonly _gasPrice: BigNumber, - _protocolFeeMultiplier: BigNumber, - ) { - this._addresses = [ - _makerAddress, - _takerAddress, - _coordinatorContract.address, - _feeRecipientAddress, - _protocolFeeCollectorAddress, - ]; - this._protocolFee = _gasPrice.times(_protocolFeeMultiplier); - } - - public async executeFillTransactionTestAsync( - orders: SignedOrder[], - transaction: SignedZeroExTransaction, - txOrigin: string, - approvalSignatures: string[], - txData: Partial, - revertError?: RevertError, - ): Promise { - const initBalances = await this._getTokenBalancesAsync(); - const tx = this._coordinatorContract.executeTransaction.awaitTransactionSuccessAsync( - transaction, - txOrigin, - transaction.signature, - approvalSignatures, - txData, - ); - - if (revertError !== undefined) { - return expect(tx).to.revertWith(revertError); - } - - const transactionReceipt = await tx; - verifyEvents(transactionReceipt, orders.map(order => this._expectedFillEvent(order)), ExchangeEvents.Fill); - - const expectedBalances = this._getExpectedBalances(initBalances, orders, transactionReceipt, txData.value); - await this._verifyBalancesAsync(expectedBalances); - } - - public async executeCancelTransactionTestAsync( - fnName: ExchangeFunctionName, - orders: SignedOrder[], - transaction: SignedZeroExTransaction, - txOrigin: string, - approvalSignatures: string[], - txData: Partial, - ): Promise { - const transactionReceipt = await this._coordinatorContract.executeTransaction.awaitTransactionSuccessAsync( - transaction, - txOrigin, - transaction.signature, - approvalSignatures, - txData, - ); - - if (fnName === ExchangeFunctionName.CancelOrdersUpTo) { - const expectedEvent: ExchangeCancelUpToEventArgs = { - makerAddress: this._makerAddress, - orderSenderAddress: this._coordinatorContract.address, - orderEpoch: new BigNumber(1), - }; - verifyEvents(transactionReceipt, [expectedEvent], ExchangeEvents.CancelUpTo); - } else { - verifyEvents( - transactionReceipt, - orders.map(order => CoordinatorTestFactory._expectedCancelEvent(order)), - ExchangeEvents.Cancel, - ); - } - } - - private async _getTokenBalancesAsync(): Promise { - const erc20Balances = await this._erc20Wrapper.getBalancesAsync(); - const ethBalances = _.zipObject( - this._addresses, - await Promise.all(this._addresses.map(address => web3Wrapper.getBalanceInWeiAsync(address))), - ); - - return { - erc20: erc20Balances, - erc721: {}, - erc1155: {}, - eth: ethBalances, - }; - } - - private _getExpectedBalances( - initBalances: TokenBalances, - orders: SignedOrder[], - txReceipt: TransactionReceiptWithDecodedLogs, - txValue?: Numberish, - ): TokenBalances { - const { erc20: erc20Balances, eth: ethBalances } = initBalances; - let remainingValue = new BigNumber(txValue || 0); - ethBalances[txReceipt.from] = ethBalances[txReceipt.from].minus(this._gasPrice.times(txReceipt.gasUsed)); - - for (const order of orders) { - const [makerAssetAddress, takerAssetAddress, makerFeeAssetAddress, takerFeeAssetAddress] = [ - order.makerAssetData, - order.takerAssetData, - order.makerFeeAssetData, - order.takerFeeAssetData, - ].map(assetData => assetDataUtils.decodeERC20AssetData(assetData).tokenAddress); - - erc20Balances[order.makerAddress][makerAssetAddress] = erc20Balances[order.makerAddress][ - makerAssetAddress - ].minus(order.makerAssetAmount); - erc20Balances[this._takerAddress][makerAssetAddress] = erc20Balances[this._takerAddress][ - makerAssetAddress - ].plus(order.makerAssetAmount); - erc20Balances[order.makerAddress][takerAssetAddress] = erc20Balances[order.makerAddress][ - takerAssetAddress - ].plus(order.takerAssetAmount); - erc20Balances[this._takerAddress][takerAssetAddress] = erc20Balances[this._takerAddress][ - takerAssetAddress - ].minus(order.takerAssetAmount); - erc20Balances[order.makerAddress][makerFeeAssetAddress] = erc20Balances[order.makerAddress][ - makerFeeAssetAddress - ].minus(order.makerFee); - erc20Balances[this._takerAddress][takerFeeAssetAddress] = erc20Balances[this._takerAddress][ - takerFeeAssetAddress - ].minus(order.takerFee); - erc20Balances[order.feeRecipientAddress][makerFeeAssetAddress] = erc20Balances[order.feeRecipientAddress][ - makerFeeAssetAddress - ].plus(order.makerFee); - erc20Balances[order.feeRecipientAddress][takerFeeAssetAddress] = erc20Balances[order.feeRecipientAddress][ - takerFeeAssetAddress - ].plus(order.takerFee); - - if (remainingValue.isGreaterThanOrEqualTo(this._protocolFee)) { - ethBalances[txReceipt.from] = ethBalances[txReceipt.from].minus(this._protocolFee); - ethBalances[this._protocolFeeCollectorAddress] = ethBalances[this._protocolFeeCollectorAddress].plus( - this._protocolFee, - ); - remainingValue = remainingValue.minus(this._protocolFee); - } else { - erc20Balances[this._takerAddress][this._wethAddress] = erc20Balances[this._takerAddress][ - this._wethAddress - ].minus(this._protocolFee); - erc20Balances[this._protocolFeeCollectorAddress][this._wethAddress] = erc20Balances[ - this._protocolFeeCollectorAddress - ][this._wethAddress].plus(this._protocolFee); - } - } - - return { - erc20: erc20Balances, - erc721: {}, - erc1155: {}, - eth: ethBalances, - }; - } - - private async _verifyBalancesAsync(expectedBalances: TokenBalances): Promise { - const { erc20: expectedErc20Balances, eth: expectedEthBalances } = expectedBalances; - const { erc20: actualErc20Balances, eth: actualEthBalances } = await this._getTokenBalancesAsync(); - const ownersByName = { - maker: this._makerAddress, - taker: this._takerAddress, - feeRecipient: this._feeRecipientAddress, - coordinator: this._coordinatorContract.address, - protocolFeeCollector: this._protocolFeeCollectorAddress, - }; - const tokensByName = { - makerAsset: this._makerAssetAddress, - takerAsset: this._takerAssetAddress, - makerFeeAsset: this._makerFeeAssetAddress, - takerFeeAsset: this._takerFeeAssetAddress, - weth: this._wethAddress, - }; - _.forIn(ownersByName, (ownerAddress, ownerName) => { - expect(actualEthBalances[ownerAddress], `${ownerName} eth balance`).to.bignumber.equal( - expectedEthBalances[ownerAddress], - ); - _.forIn(tokensByName, (tokenAddress, tokenName) => { - expect( - actualErc20Balances[ownerAddress][tokenAddress], - `${ownerName} ${tokenName} balance`, - ).to.bignumber.equal(expectedErc20Balances[ownerAddress][tokenAddress]); - }); - }); - } - - private _expectedFillEvent(order: SignedOrder): ExchangeFillEventArgs { - return { - makerAddress: order.makerAddress, - takerAddress: this._takerAddress, - senderAddress: order.senderAddress, - feeRecipientAddress: order.feeRecipientAddress, - makerAssetData: order.makerAssetData, - takerAssetData: order.takerAssetData, - makerFeeAssetData: order.makerFeeAssetData, - takerFeeAssetData: order.takerFeeAssetData, - makerAssetFilledAmount: order.makerAssetAmount, - takerAssetFilledAmount: order.takerAssetAmount, - makerFeePaid: order.makerFee, - takerFeePaid: order.takerFee, - protocolFeePaid: this._protocolFee, - orderHash: orderHashUtils.getOrderHashHex(order), - }; - } -} diff --git a/contracts/integrations/test/coordinator/deploy_coordinator.ts b/contracts/integrations/test/coordinator/deploy_coordinator.ts new file mode 100644 index 0000000000..031af5177c --- /dev/null +++ b/contracts/integrations/test/coordinator/deploy_coordinator.ts @@ -0,0 +1,20 @@ +import { artifacts, CoordinatorContract } from '@0x/contracts-coordinator'; +import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange'; +import { BlockchainTestsEnvironment } from '@0x/contracts-test-utils'; +import { BigNumber } from '@0x/utils'; + +import { DeploymentManager } from '../utils/deployment_manager'; + +export async function deployCoordinatorAsync( + deployment: DeploymentManager, + environment: BlockchainTestsEnvironment, +): Promise { + return await CoordinatorContract.deployFrom0xArtifactAsync( + artifacts.Coordinator, + environment.provider, + deployment.txDefaults, + { ...exchangeArtifacts, ...artifacts }, + deployment.exchange.address, + new BigNumber(deployment.chainId), + ); +} diff --git a/contracts/integrations/test/utils/deployment_manager.ts b/contracts/integrations/test/utils/deployment_manager.ts index 115bc435e3..ae3a9ad32a 100644 --- a/contracts/integrations/test/utils/deployment_manager.ts +++ b/contracts/integrations/test/utils/deployment_manager.ts @@ -122,7 +122,9 @@ export interface DeploymentOptions { } export class DeploymentManager { - public static protocolFeeMultiplier = new BigNumber(150000); + public static readonly protocolFeeMultiplier = new BigNumber(150000); + public static readonly gasPrice = new BigNumber(1e9); // 1 Gwei + public static readonly protocolFee = DeploymentManager.gasPrice.times(DeploymentManager.protocolFeeMultiplier); /** * Fully deploy the 0x exchange and staking contracts and configure the system with the @@ -141,6 +143,7 @@ export class DeploymentManager { const txDefaults = { ...environment.txDefaults, from: owner, + gasPrice: DeploymentManager.gasPrice, }; // Deploy the contracts using the same owner and environment. @@ -148,7 +151,7 @@ export class DeploymentManager { const exchange = await ExchangeContract.deployFrom0xArtifactAsync( exchangeArtifacts.Exchange, environment.provider, - environment.txDefaults, + txDefaults, { ...ERC20Artifacts, ...exchangeArtifacts }, new BigNumber(chainId), ); @@ -200,7 +203,7 @@ export class DeploymentManager { staking.stakingProxy, ]); - return new DeploymentManager(assetProxies, governor, exchange, staking, tokens, chainId, accounts); + return new DeploymentManager(assetProxies, governor, exchange, staking, tokens, chainId, accounts, txDefaults); } /** @@ -493,5 +496,6 @@ export class DeploymentManager { public tokens: TokenContracts, public chainId: number, public accounts: string[], + public txDefaults: Partial, ) {} } diff --git a/tsconfig.json b/tsconfig.json index c4d37eb392..400566c2d0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,7 +17,7 @@ "sourceMap": true }, // These are not working right now - "exclude": ["./contracts/extensions/**/*", "./contracts/coordinator/**/*"], + "exclude": ["./contracts/extensions/**/*"], // The root of the project is just a list of references and does not contain // any top-level TypeScript code. "include": [], @@ -26,6 +26,7 @@ { "path": "./contracts/erc20" }, { "path": "./contracts/erc721" }, { "path": "./contracts/exchange" }, + { "path": "./contracts/coordinator" }, { "path": "./contracts/exchange-forwarder" }, { "path": "./contracts/exchange-libs" }, // { "path": "./contracts/extensions" }, @@ -33,6 +34,7 @@ { "path": "./contracts/test-utils" }, { "path": "./contracts/utils" }, { "path": "./contracts/dev-utils" }, + { "path": "./contracts/integrations" }, { "path": "./packages/0x.js" }, { "path": "./packages/abi-gen-wrappers" }, { "path": "./packages/abi-gen" },