diff --git a/contracts/integrations/test/framework/actors/base.ts b/contracts/integrations/test/framework/actors/base.ts index 63deb6bc4e..bce2a3612c 100644 --- a/contracts/integrations/test/framework/actors/base.ts +++ b/contracts/integrations/test/framework/actors/base.ts @@ -1,3 +1,4 @@ +import { ERC1155MintableContract } from '@0x/contracts-erc1155'; import { DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20'; import { DummyERC721TokenContract } from '@0x/contracts-erc721'; import { constants, getRandomInteger, TransactionFactory } from '@0x/contracts-test-utils'; @@ -95,6 +96,37 @@ export class Actor { return tokenIds; } + /** + * Mints some number of ERC1115 fungible tokens and approves a spender (defaults to the ERC1155 asset proxy) + * to transfer the token. + */ + public async configureERC1155TokenAsync( + token: ERC1155MintableContract, + spender?: string, + amount?: BigNumber, + ): Promise { + // Create a fungible token. + const id = await token.create.callAsync('', false, { from: this.address }); + await token.create.awaitTransactionSuccessAsync('', false, { from: this.address }); + + // Mint the token + await token.mintFungible.awaitTransactionSuccessAsync( + id, + [this.address], + [amount || constants.INITIAL_ERC20_BALANCE], + { from: this.address }, + ); + + // Set approval for all token types for the spender. + await token.setApprovalForAll.awaitTransactionSuccessAsync( + spender || this.deployment.assetProxies.erc1155Proxy.address, + true, + { from: this.address }, + ); + + return id; + } + /** * Signs a transaction. */ diff --git a/contracts/integrations/test/internal-integration-tests/match_orders_test.ts b/contracts/integrations/test/internal-integration-tests/match_orders_test.ts index 487ff4a861..096b3cfdbb 100644 --- a/contracts/integrations/test/internal-integration-tests/match_orders_test.ts +++ b/contracts/integrations/test/internal-integration-tests/match_orders_test.ts @@ -1,5 +1,5 @@ import { DevUtilsContract } from '@0x/contracts-dev-utils'; -import { BlockchainBalanceStore } from '@0x/contracts-exchange'; +import { BlockchainBalanceStore, TokenIds } from '@0x/contracts-exchange'; import { ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs'; import { toBaseUnitAmount } from '@0x/contracts-staking'; import { blockchainTests, constants, expect } from '@0x/contracts-test-utils'; @@ -17,23 +17,29 @@ const { isRoundingErrorCeil, isRoundingErrorFloor } = LibReferenceFunctions; const ZERO = constants.ZERO_AMOUNT; -blockchainTests.resets.only('matchOrders', env => { +blockchainTests.resets('matchOrders', env => { + // The fee recipient addresses. let feeRecipientLeft: Actor; let feeRecipientRight: Actor; + // The address that should be responsible for matching orders. let matcher: Actor; + // Market makers who have opposite maker and taker assets. let makerLeft: Maker; let makerRight: Maker; + // The addresses of important assets for testing. let makerAssetAddressLeft: string; let makerAssetAddressRight: string; let feeAssetAddress: string; + let deployment: DeploymentManager; let matchOrderTester: MatchOrderTester; - const devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider, txDefaults); - let deployment: DeploymentManager; + const devUtils = new DevUtilsContract(constants.NULL_ADDRESS, env.provider, env.txDefaults); + let leftId: BigNumber; + let rightId: BigNumber; const PROTOCOL_FEE = DeploymentManager.protocolFeeMultiplier.times(constants.DEFAULT_GAS_PRICE); @@ -99,6 +105,13 @@ blockchainTests.resets.only('matchOrders', env => { feeRecipientRight.configureERC20TokenAsync(deployment.tokens.weth), ]); + leftId = await makerLeft.configureERC1155TokenAsync(deployment.tokens.erc1155[0]); + [rightId] = await makerRight.configureERC721TokenAsync(deployment.tokens.erc721[0]); + + const tokenIds: TokenIds = { erc721: {}, erc1155: {} }; + tokenIds.erc1155[deployment.tokens.erc1155[0].address] = { fungible: [leftId], nonFungible: [] }; + tokenIds.erc721[deployment.tokens.erc721[0].address] = [rightId]; + const blockchainBalanceStore = new BlockchainBalanceStore( { feeRecipientLeft: feeRecipientLeft.address, @@ -115,8 +128,14 @@ blockchainTests.resets.only('matchOrders', env => { feeToken: deployment.tokens.erc20[2], weth: deployment.tokens.weth, }, + erc721: { + token: deployment.tokens.erc721[0], + }, + erc1155: { + token: deployment.tokens.erc1155[0], + }, }, - {}, + tokenIds, ); matchOrderTester = new MatchOrderTester(deployment, blockchainBalanceStore); @@ -2418,39 +2437,6 @@ blockchainTests.resets.only('matchOrders', env => { }); }); - /* FIXME - const leftMakerAssetAmount = _.includes(fungibleTypes, combo.leftMaker) - ? toBaseUnitAmount(15, 18) - : toBaseUnitAmount(1, 0); - const leftTakerAssetAmount = _.includes(fungibleTypes, combo.rightMaker) - ? toBaseUnitAmount(30, 18) - : toBaseUnitAmount(1, 0); - const rightMakerAssetAmount = _.includes(fungibleTypes, combo.rightMaker) - ? toBaseUnitAmount(30, 18) - : toBaseUnitAmount(1, 0); - const rightTakerAssetAmount = _.includes(fungibleTypes, combo.leftMaker) - ? toBaseUnitAmount(14, 18) - : toBaseUnitAmount(1, 0); - const leftMakerFeeAssetAmount = _.includes(fungibleTypes, combo.leftMakerFee) - ? toBaseUnitAmount(8, 12) - : toBaseUnitAmount(1, 0); - const rightMakerFeeAssetAmount = _.includes(fungibleTypes, combo.rightMakerFee) - ? toBaseUnitAmount(7, 12) - : toBaseUnitAmount(1, 0); - const leftTakerFeeAssetAmount = _.includes(fungibleTypes, combo.leftTakerFee) - ? toBaseUnitAmount(6, 12) - : toBaseUnitAmount(1, 0); - const rightTakerFeeAssetAmount = _.includes(fungibleTypes, combo.rightTakerFee) - ? toBaseUnitAmount(5, 12) - : toBaseUnitAmount(1, 0); - const leftMakerAssetReceivedByTakerAmount = _.includes(fungibleTypes, combo.leftMaker) - ? leftMakerAssetAmount.minus(rightTakerAssetAmount) - : toBaseUnitAmount(0, 0); - const rightMakerAssetReceivedByTakerAmount = _.includes(fungibleTypes, combo.leftMaker) - ? rightMakerAssetAmount.minus(leftTakerAssetAmount) - : toBaseUnitAmount(0, 0); - */ - describe('batchMatchOrders and batchMatchOrdersWithMaximalFill rich errors', async () => { it('should fail if there are zero leftOrders with the ZeroLeftOrders rich error reason', async () => { const leftOrders: SignedOrder[] = []; @@ -3149,5 +3135,58 @@ blockchainTests.resets.only('matchOrders', env => { ); }); }); + + describe('token sanity checks', () => { + it('should be able to match ERC721 tokens with ERC1155 tokens', async () => { + const leftMakerAssetData = assetDataUtils.encodeERC1155AssetData( + deployment.tokens.erc1155[0].address, + [leftId], + [new BigNumber(1)], + '0x', + ); + const rightMakerAssetData = assetDataUtils.encodeERC721AssetData( + deployment.tokens.erc721[0].address, + rightId, + ); + + const signedOrderLeft = await makerLeft.signOrderAsync({ + makerAssetAmount: toBaseUnitAmount(100, 0), + takerAssetAmount: toBaseUnitAmount(1, 0), + makerAssetData: leftMakerAssetData, + takerAssetData: rightMakerAssetData, + }); + const signedOrderRight = await makerRight.signOrderAsync({ + makerAssetAmount: toBaseUnitAmount(1, 0), + takerAssetAmount: toBaseUnitAmount(100, 0), + makerAssetData: rightMakerAssetData, + takerAssetData: leftMakerAssetData, + }); + + const expectedTransferAmounts = { + // Left Maker + leftMakerAssetSoldByLeftMakerAmount: toBaseUnitAmount(100, 0), + leftMakerFeeAssetPaidByLeftMakerAmount: toBaseUnitAmount(100, 16), // 100% + // Right Maker + rightMakerAssetSoldByRightMakerAmount: toBaseUnitAmount(1, 0), + rightMakerFeeAssetPaidByRightMakerAmount: toBaseUnitAmount(100, 16), // 100% + // Taker + leftTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100% + rightTakerFeeAssetPaidByTakerAmount: toBaseUnitAmount(100, 16), // 100% + leftProtocolFeePaidByTakerAmount: PROTOCOL_FEE, + rightProtocolFeePaidByTakerAmount: PROTOCOL_FEE, + }; + + await matchOrderTester.matchOrdersAndAssertEffectsAsync( + { + leftOrder: signedOrderLeft, + rightOrder: signedOrderRight, + }, + expectedTransferAmounts, + matcher.address, + PROTOCOL_FEE.times(2), + false, + ); + }); + }); }); // tslint:disable-line:max-file-line-count diff --git a/contracts/integrations/test/utils/match_order_tester.ts b/contracts/integrations/test/utils/match_order_tester.ts index 60100a21d4..b33ee719e6 100644 --- a/contracts/integrations/test/utils/match_order_tester.ts +++ b/contracts/integrations/test/utils/match_order_tester.ts @@ -1,6 +1,6 @@ import { DevUtilsContract } from '@0x/contracts-dev-utils'; import { BlockchainBalanceStore, ExchangeContract, LocalBalanceStore } from '@0x/contracts-exchange'; -import { constants, expect, OrderStatus } from '@0x/contracts-test-utils'; +import { constants, expect, OrderStatus, TransactionHelper } from '@0x/contracts-test-utils'; import { orderHashUtils } from '@0x/order-utils'; import { BatchMatchedFillResults, FillResults, MatchedFillResults, SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; @@ -244,9 +244,9 @@ export class MatchOrderTester { orders, takerAddress, this._deployment.staking.stakingProxy.address, - this._blockchainBalanceStore, toFullMatchTransferAmounts(expectedTransferAmounts), this._devUtils, + this._blockchainBalanceStore, localBalanceStore, ); const expectedResults = convertToMatchResults(expectedMatchResults); @@ -377,16 +377,15 @@ async function simulateBatchMatchOrdersAsync( } } - // FIXME - These arguments should be reordered // Add the latest match to the batch match results batchMatchResults.matches.push( await simulateMatchOrdersAsync( matchedOrders, takerAddress, stakingProxyAddress, - blockchainBalanceStore, toFullMatchTransferAmounts(transferAmounts[i]), devUtils, + blockchainBalanceStore, localBalanceStore, ), ); @@ -434,7 +433,6 @@ async function simulateBatchMatchOrdersAsync( return batchMatchResults; } -// FIXME - Is it possible to remove `transferAmounts` /** * Simulates matching two orders by transferring amounts defined in * `transferAmounts` and returns the results. @@ -448,9 +446,9 @@ async function simulateMatchOrdersAsync( orders: MatchedOrders, takerAddress: string, stakingProxyAddress: string, - blockchainBalanceStore: BlockchainBalanceStore, // FIXME - Is this right? transferAmounts: MatchTransferAmounts, devUtils: DevUtilsContract, + blockchainBalanceStore: BlockchainBalanceStore, localBalanceStore: LocalBalanceStore, ): Promise { // prettier-ignore @@ -478,7 +476,6 @@ async function simulateMatchOrdersAsync( orders.rightOrder.makerAssetData, ); - // FIXME - Is this a necessary condition? if (orders.leftOrder.makerAddress !== orders.leftOrder.feeRecipientAddress) { // Left maker fees localBalanceStore.transferAsset( @@ -489,7 +486,6 @@ async function simulateMatchOrdersAsync( ); } - // FIXME - Is this a necessary condition? // Left maker asset -> right maker localBalanceStore.transferAsset( orders.leftOrder.makerAddress, @@ -498,7 +494,6 @@ async function simulateMatchOrdersAsync( orders.leftOrder.makerAssetData, ); - // FIXME - Is this a necessary condition? if (orders.rightOrder.makerAddress !== orders.rightOrder.feeRecipientAddress) { // Right maker fees localBalanceStore.transferAsset( @@ -676,7 +671,6 @@ function simulateFillEvents( ]; } -// FIXME - Refactor this to use filterToLogsArguments /** * Extract `Fill` events from a transaction receipt. */ @@ -830,7 +824,6 @@ function getLastMatch(batchMatchResults: BatchMatchResults): MatchResults { return batchMatchResults.matches[batchMatchResults.matches.length - 1]; } -// FIXME - This can probably be removed in favor of the reference functions. /** * Add a new fill results object to a total fill results object destructively. * @param total The total fill results that should be updated. @@ -841,6 +834,7 @@ function addFillResults(total: FillEventArgs, fill: FillEventArgs): void { expect(total.orderHash).to.be.eq(fill.orderHash); expect(total.makerAddress).to.be.eq(fill.makerAddress); expect(total.takerAddress).to.be.eq(fill.takerAddress); + // Add the fill results together total.makerAssetFilledAmount = total.makerAssetFilledAmount.plus(fill.makerAssetFilledAmount); total.takerAssetFilledAmount = total.takerAssetFilledAmount.plus(fill.takerAssetFilledAmount);