diff --git a/contracts/dev-utils/compiler.json b/contracts/dev-utils/compiler.json index c922e1363b..f3a3413f4a 100644 --- a/contracts/dev-utils/compiler.json +++ b/contracts/dev-utils/compiler.json @@ -27,6 +27,7 @@ "src/DevUtils.sol", "src/LibAssetData.sol", "src/LibTransactionDecoder.sol", - "src/EthBalanceChecker.sol" + "src/EthBalanceChecker.sol", + "src/OrderTransferSimulationUtils.sol" ] } diff --git a/contracts/dev-utils/package.json b/contracts/dev-utils/package.json index 576dbffc69..e5a3699e20 100644 --- a/contracts/dev-utils/package.json +++ b/contracts/dev-utils/package.json @@ -34,7 +34,7 @@ "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol" }, "config": { - "abis": "./generated-artifacts/@(DevUtils|LibAssetData|LibTransactionDecoder|EthBalanceChecker).json", + "abis": "./generated-artifacts/@(DevUtils|LibAssetData|LibTransactionDecoder|EthBalanceChecker|OrderTransferSimulationUtils).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/dev-utils/src/artifacts.ts b/contracts/dev-utils/src/artifacts.ts index 3c2f022c66..8356ce7563 100644 --- a/contracts/dev-utils/src/artifacts.ts +++ b/contracts/dev-utils/src/artifacts.ts @@ -8,8 +8,10 @@ import { ContractArtifact } from 'ethereum-types'; import * as DevUtils from '../generated-artifacts/DevUtils.json'; import * as LibAssetData from '../generated-artifacts/LibAssetData.json'; import * as LibTransactionDecoder from '../generated-artifacts/LibTransactionDecoder.json'; +import * as OrderTransferSimulationUtils from '../generated-artifacts/OrderTransferSimulationUtils.json'; export const artifacts = { DevUtils: DevUtils as ContractArtifact, LibAssetData: LibAssetData as ContractArtifact, LibTransactionDecoder: LibTransactionDecoder as ContractArtifact, + OrderTransferSimulationUtils: OrderTransferSimulationUtils as ContractArtifact, }; diff --git a/contracts/dev-utils/src/wrappers.ts b/contracts/dev-utils/src/wrappers.ts index e07c65436d..13c6397a9d 100644 --- a/contracts/dev-utils/src/wrappers.ts +++ b/contracts/dev-utils/src/wrappers.ts @@ -6,3 +6,4 @@ export * from '../generated-wrappers/dev_utils'; export * from '../generated-wrappers/lib_asset_data'; export * from '../generated-wrappers/lib_transaction_decoder'; +export * from '../generated-wrappers/order_transfer_simulation_utils'; diff --git a/contracts/dev-utils/test/order_validation_utils.ts b/contracts/dev-utils/test/order_validation_utils.ts index 725e4ccbe2..0e08a28b82 100644 --- a/contracts/dev-utils/test/order_validation_utils.ts +++ b/contracts/dev-utils/test/order_validation_utils.ts @@ -20,7 +20,7 @@ import { } from '@0x/contracts-test-utils'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; -import { SignedOrder } from '@0x/types'; +import { OrderTransferResults, SignedOrder } from '@0x/types'; import { BigNumber, providerUtils } from '@0x/utils'; import * as chai from 'chai'; @@ -30,7 +30,7 @@ chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -describe('OrderValidationUtils', () => { +describe('OrderValidationUtils/OrderTransferSimulatorUtils', () => { let makerAddress: string; let takerAddress: string; let owner: string; @@ -412,6 +412,7 @@ describe('OrderValidationUtils', () => { }); describe('getOrderRelevantStates', async () => { it('should return the correct information for multiple orders', async () => { + signedOrder = await orderFactory.newSignedOrderAsync(); await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount); await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { from: makerAddress, @@ -443,5 +444,149 @@ describe('OrderValidationUtils', () => { expect(isValidSignature[1]).to.equal(false); }); }); + describe('getSimulatedOrderTransferResults', () => { + beforeEach(async () => { + signedOrder = await orderFactory.newSignedOrderAsync(); + }); + it('should return TakerAssetDataFailed if the takerAsset transfer fails', async () => { + const orderTransferResults = await devUtils.getSimulatedOrderTransferResults.callAsync( + signedOrder, + takerAddress, + signedOrder.takerAssetAmount, + ); + expect(orderTransferResults).to.equal(OrderTransferResults.TakerAssetDataFailed); + }); + it('should return MakerAssetDataFailed if the makerAsset transfer fails', async () => { + await erc20Token2.setBalance.awaitTransactionSuccessAsync(takerAddress, signedOrder.takerAssetAmount, { + from: owner, + }); + await erc20Token2.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.takerAssetAmount, { + from: takerAddress, + }); + const orderTransferResults = await devUtils.getSimulatedOrderTransferResults.callAsync( + signedOrder, + takerAddress, + signedOrder.takerAssetAmount, + ); + expect(orderTransferResults).to.equal(OrderTransferResults.MakerAssetDataFailed); + }); + it('should return TakerFeeAssetDataFailed if the takerFeeAsset transfer fails', async () => { + await erc20Token2.setBalance.awaitTransactionSuccessAsync(takerAddress, signedOrder.takerAssetAmount, { + from: owner, + }); + await erc20Token2.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.takerAssetAmount, { + from: takerAddress, + }); + await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount, { + from: owner, + }); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { + from: makerAddress, + }); + const orderTransferResults = await devUtils.getSimulatedOrderTransferResults.callAsync( + signedOrder, + takerAddress, + signedOrder.takerAssetAmount, + ); + expect(orderTransferResults).to.equal(OrderTransferResults.TakerFeeAssetDataFailed); + }); + it('should return MakerFeeAssetDataFailed if the makerFeeAsset transfer fails', async () => { + await erc20Token2.setBalance.awaitTransactionSuccessAsync(takerAddress, signedOrder.takerAssetAmount, { + from: owner, + }); + await erc20Token2.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.takerAssetAmount, { + from: takerAddress, + }); + await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount, { + from: owner, + }); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { + from: makerAddress, + }); + await feeErc20Token.setBalance.awaitTransactionSuccessAsync(takerAddress, signedOrder.takerFee, { + from: owner, + }); + await feeErc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.takerFee, { + from: takerAddress, + }); + const orderTransferResults = await devUtils.getSimulatedOrderTransferResults.callAsync( + signedOrder, + takerAddress, + signedOrder.takerAssetAmount, + ); + expect(orderTransferResults).to.equal(OrderTransferResults.MakerFeeAssetDataFailed); + }); + it('should return TransfersSuccessful if all transfers succeed', async () => { + await erc20Token2.setBalance.awaitTransactionSuccessAsync(takerAddress, signedOrder.takerAssetAmount, { + from: owner, + }); + await erc20Token2.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.takerAssetAmount, { + from: takerAddress, + }); + await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount, { + from: owner, + }); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { + from: makerAddress, + }); + await feeErc20Token.setBalance.awaitTransactionSuccessAsync(takerAddress, signedOrder.takerFee, { + from: owner, + }); + await feeErc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.takerFee, { + from: takerAddress, + }); + await feeErc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee, { + from: owner, + }); + await feeErc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { + from: makerAddress, + }); + const orderTransferResults = await devUtils.getSimulatedOrderTransferResults.callAsync( + signedOrder, + takerAddress, + signedOrder.takerAssetAmount, + ); + expect(orderTransferResults).to.equal(OrderTransferResults.TransfersSuccessful); + }); + }); + describe('getSimulatedOrdersTransferResults', async () => { + it('should simulate the transfers of each order independently from one another', async () => { + // Set balances and allowances to exactly enough to fill a single order + await erc20Token2.setBalance.awaitTransactionSuccessAsync(takerAddress, signedOrder.takerAssetAmount, { + from: owner, + }); + await erc20Token2.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.takerAssetAmount, { + from: takerAddress, + }); + await erc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerAssetAmount, { + from: owner, + }); + await erc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerAssetAmount, { + from: makerAddress, + }); + await feeErc20Token.setBalance.awaitTransactionSuccessAsync(takerAddress, signedOrder.takerFee, { + from: owner, + }); + await feeErc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.takerFee, { + from: takerAddress, + }); + await feeErc20Token.setBalance.awaitTransactionSuccessAsync(makerAddress, signedOrder.makerFee, { + from: owner, + }); + await feeErc20Token.approve.awaitTransactionSuccessAsync(erc20Proxy.address, signedOrder.makerFee, { + from: makerAddress, + }); + const [ + orderTransferResults1, + orderTransferResults2, + ] = await devUtils.getSimulatedOrdersTransferResults.callAsync( + [signedOrder, signedOrder], + [takerAddress, takerAddress], + [signedOrder.takerAssetAmount, signedOrder.takerAssetAmount], + ); + expect(orderTransferResults1).to.equal(OrderTransferResults.TransfersSuccessful); + expect(orderTransferResults2).to.equal(OrderTransferResults.TransfersSuccessful); + }); + }); }); // tslint:disable:max-file-line-count diff --git a/contracts/dev-utils/tsconfig.json b/contracts/dev-utils/tsconfig.json index 2f16c556bf..97fefca01e 100644 --- a/contracts/dev-utils/tsconfig.json +++ b/contracts/dev-utils/tsconfig.json @@ -6,7 +6,8 @@ "generated-artifacts/DevUtils.json", "generated-artifacts/LibAssetData.json", "generated-artifacts/LibTransactionDecoder.json", - "generated-artifacts/EthBalanceChecker.json" + "generated-artifacts/EthBalanceChecker.json", + "generated-artifacts/OrderTransferSimulationUtils.json" ], "exclude": ["./deploy/solc/solc_bin"] } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index eafe88d321..e39ebda130 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -803,3 +803,11 @@ export enum OrderStatus { FullyFilled, Cancelled, } + +export enum OrderTransferResults { + TakerAssetDataFailed, + MakerAssetDataFailed, + TakerFeeAssetDataFailed, + MakerFeeAssetDataFailed, + TransfersSuccessful, +}