diff --git a/contracts/exchange/test/core.ts b/contracts/exchange/test/core.ts index ff6874c0ea..79b9d06185 100644 --- a/contracts/exchange/test/core.ts +++ b/contracts/exchange/test/core.ts @@ -6,6 +6,8 @@ import { ERC721ProxyContract, ERC721Wrapper, MultiAssetProxyContract, + StaticCallProxyContract, + TestStaticCallTargetContract, } from '@0x/contracts-asset-proxy'; import { ERC1155MintableContract } from '@0x/contracts-erc1155'; import { @@ -69,6 +71,8 @@ describe('Exchange core', () => { let erc721Proxy: ERC721ProxyContract; let erc1155Proxy: ERC721ProxyContract; let multiAssetProxy: MultiAssetProxyContract; + let staticCallProxy: StaticCallProxyContract; + let staticCallTarget: TestStaticCallTargetContract; let maliciousWallet: TestStaticCallReceiverContract; let maliciousValidator: TestStaticCallReceiverContract; let erc1155Contract: ERC1155MintableContract; @@ -113,6 +117,11 @@ describe('Exchange core', () => { provider, txDefaults, ); + staticCallProxy = await StaticCallProxyContract.deployFrom0xArtifactAsync( + proxyArtifacts.StaticCallProxy, + provider, + txDefaults, + ); const numDummyErc20ToDeploy = 3; [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( numDummyErc20ToDeploy, @@ -141,66 +150,22 @@ describe('Exchange core', () => { ); // Configure ERC20Proxy - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); + await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(exchange.address, { from: owner }); + await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(multiAssetProxy.address, { from: owner }); // Configure ERC721Proxy - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); + await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(exchange.address, { from: owner }); + await erc721Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(multiAssetProxy.address, { from: owner }); // Configure ERC1155Proxy - await web3Wrapper.awaitTransactionSuccessAsync( - await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc1155Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); + await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(exchange.address, { from: owner }); + await erc1155Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(multiAssetProxy.address, { from: owner }); // Configure MultiAssetProxy - await web3Wrapper.awaitTransactionSuccessAsync( - await multiAssetProxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc721Proxy.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); + await multiAssetProxy.addAuthorizedAddress.awaitTransactionSuccessAsync(exchange.address, { from: owner }); + await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(erc20Proxy.address, { from: owner }); + await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(erc721Proxy.address, { from: owner }); + await multiAssetProxy.registerAssetProxy.awaitTransactionSuccessAsync(staticCallProxy.address, { from: owner }); // Configure Exchange exchangeWrapper = new ExchangeWrapper(exchange, provider); @@ -208,6 +173,7 @@ describe('Exchange core', () => { await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner); await exchangeWrapper.registerAssetProxyAsync(erc1155Proxy.address, owner); await exchangeWrapper.registerAssetProxyAsync(multiAssetProxy.address, owner); + await exchangeWrapper.registerAssetProxyAsync(staticCallProxy.address, owner); // Configure ERC20 tokens await erc20Wrapper.setBalancesAndAllowancesAsync(); @@ -268,10 +234,7 @@ describe('Exchange core', () => { signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), }); - await web3Wrapper.awaitTransactionSuccessAsync( - await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), - constants.AWAIT_TRANSACTION_MINED_MS, - ); + await reentrantErc20Token.setCurrentFunction.awaitTransactionSuccessAsync(functionId); await expectTransactionFailedAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), RevertReason.TransferFailed, @@ -310,20 +273,14 @@ describe('Exchange core', () => { it('should revert if `isValidSignature` tries to update state when SignatureType=Wallet', async () => { const maliciousMakerAddress = maliciousWallet.address; - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20TokenA.setBalance.sendTransactionAsync( - maliciousMakerAddress, - constants.INITIAL_ERC20_BALANCE, - ), - constants.AWAIT_TRANSACTION_MINED_MS, + await erc20TokenA.setBalance.awaitTransactionSuccessAsync( + maliciousMakerAddress, + constants.INITIAL_ERC20_BALANCE, ); - await web3Wrapper.awaitTransactionSuccessAsync( - await maliciousWallet.approveERC20.sendTransactionAsync( - erc20TokenA.address, - erc20Proxy.address, - constants.INITIAL_ERC20_ALLOWANCE, - ), - constants.AWAIT_TRANSACTION_MINED_MS, + await maliciousWallet.approveERC20.awaitTransactionSuccessAsync( + erc20TokenA.address, + erc20Proxy.address, + constants.INITIAL_ERC20_ALLOWANCE, ); signedOrder = await orderFactory.newSignedOrderAsync({ makerAddress: maliciousMakerAddress, @@ -338,13 +295,10 @@ describe('Exchange core', () => { it('should revert if `isValidSignature` tries to update state when SignatureType=Validator', async () => { const isApproved = true; - await web3Wrapper.awaitTransactionSuccessAsync( - await exchange.setSignatureValidatorApproval.sendTransactionAsync( - maliciousValidator.address, - isApproved, - { from: makerAddress }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, + await exchange.setSignatureValidatorApproval.awaitTransactionSuccessAsync( + maliciousValidator.address, + isApproved, + { from: makerAddress }, ); signedOrder.signature = `${maliciousValidator.address}0${SignatureType.Validator}`; await expectTransactionFailedAsync( @@ -399,17 +353,14 @@ describe('Exchange core', () => { constants.DUMMY_TOKEN_DECIMALS, constants.DUMMY_TOKEN_TOTAL_SUPPLY, ); - await web3Wrapper.awaitTransactionSuccessAsync( - await noReturnErc20Token.setBalance.sendTransactionAsync(makerAddress, constants.INITIAL_ERC20_BALANCE), - constants.AWAIT_TRANSACTION_MINED_MS, + await noReturnErc20Token.setBalance.awaitTransactionSuccessAsync( + makerAddress, + constants.INITIAL_ERC20_BALANCE, ); - await web3Wrapper.awaitTransactionSuccessAsync( - await noReturnErc20Token.approve.sendTransactionAsync( - erc20Proxy.address, - constants.INITIAL_ERC20_ALLOWANCE, - { from: makerAddress }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, + await noReturnErc20Token.approve.awaitTransactionSuccessAsync( + erc20Proxy.address, + constants.INITIAL_ERC20_ALLOWANCE, + { from: makerAddress }, ); }); it('should transfer the correct amounts when makerAssetAmount === takerAssetAmount', async () => { @@ -1516,6 +1467,122 @@ describe('Exchange core', () => { }); }); + describe('Testing orders that utilize StaticCallProxy', () => { + before(async () => { + staticCallTarget = await TestStaticCallTargetContract.deployFrom0xArtifactAsync( + proxyArtifacts.TestStaticCallTarget, + provider, + txDefaults, + ); + }); + it('should revert if the staticcall is unsuccessful', async () => { + const staticCallData = staticCallTarget.assertEvenNumber.getABIEncodedTransactionData(new BigNumber(1)); + const assetData = assetDataUtils.encodeStaticCallAssetData( + staticCallTarget.address, + staticCallData, + constants.KECCAK256_NULL, + ); + signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: assetData }); + await expectTransactionFailedAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.TargetNotEven, + ); + }); + it('should fill the order if the staticcall is successful', async () => { + const staticCallData = staticCallTarget.assertEvenNumber.getABIEncodedTransactionData( + constants.ZERO_AMOUNT, + ); + const assetData = assetDataUtils.encodeStaticCallAssetData( + staticCallTarget.address, + staticCallData, + constants.KECCAK256_NULL, + ); + signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: assetData }); + + const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); + const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); + const initialFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress); + const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); + const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); + + await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); + + const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); + const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); + const finalFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress); + const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); + const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); + + expect(finalMakerZrxBalance).to.bignumber.equal(initialMakerZrxBalance.minus(signedOrder.makerFee)); + expect(finalTakerZrxBalance).to.bignumber.equal(initialTakerZrxBalance.minus(signedOrder.takerFee)); + expect(finalFeeRecipientZrxBalance).to.bignumber.equal( + initialFeeRecipientZrxBalance.plus(signedOrder.makerFee).plus(signedOrder.takerFee), + ); + expect(finalMakerBalanceB).to.bignumber.equal(initialMakerBalanceB.plus(signedOrder.takerAssetAmount)); + expect(finalTakerBalanceB).to.bignumber.equal(initialTakerBalanceB.minus(signedOrder.takerAssetAmount)); + }); + it('should revert if the staticcall is unsuccessful using the MultiAssetProxy', async () => { + const staticCallData = staticCallTarget.assertEvenNumber.getABIEncodedTransactionData(new BigNumber(1)); + const staticCallAssetData = assetDataUtils.encodeStaticCallAssetData( + staticCallTarget.address, + staticCallData, + constants.KECCAK256_NULL, + ); + const assetData = assetDataUtils.encodeMultiAssetData( + [new BigNumber(1), new BigNumber(1)], + [assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), staticCallAssetData], + ); + signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: assetData }); + await expectTransactionFailedAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.TargetNotEven, + ); + }); + it('should fill the order is the staticcall is successful using the MultiAssetProxy', async () => { + const staticCallData = staticCallTarget.assertEvenNumber.getABIEncodedTransactionData( + constants.ZERO_AMOUNT, + ); + const staticCallAssetData = assetDataUtils.encodeStaticCallAssetData( + staticCallTarget.address, + staticCallData, + constants.KECCAK256_NULL, + ); + const assetData = assetDataUtils.encodeMultiAssetData( + [new BigNumber(1), new BigNumber(1)], + [assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), staticCallAssetData], + ); + signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData: assetData }); + + const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); + const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); + const initialFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress); + const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); + const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); + const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); + const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); + + await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); + + const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); + const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); + const finalFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress); + const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); + const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); + const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); + const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); + + expect(finalMakerZrxBalance).to.bignumber.equal(initialMakerZrxBalance.minus(signedOrder.makerFee)); + expect(finalTakerZrxBalance).to.bignumber.equal(initialTakerZrxBalance.minus(signedOrder.takerFee)); + expect(finalFeeRecipientZrxBalance).to.bignumber.equal( + initialFeeRecipientZrxBalance.plus(signedOrder.makerFee).plus(signedOrder.takerFee), + ); + expect(finalMakerBalanceA).to.bignumber.equal(initialMakerBalanceA.minus(signedOrder.makerAssetAmount)); + expect(finalTakerBalanceA).to.bignumber.equal(initialTakerBalanceA.plus(signedOrder.makerAssetAmount)); + expect(finalMakerBalanceB).to.bignumber.equal(initialMakerBalanceB.plus(signedOrder.takerAssetAmount)); + expect(finalTakerBalanceB).to.bignumber.equal(initialTakerBalanceB.minus(signedOrder.takerAssetAmount)); + }); + }); + describe('getOrderInfo', () => { beforeEach(async () => { signedOrder = await orderFactory.newSignedOrderAsync(); diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts index 64ca45a84e..8aeb54646c 100644 --- a/packages/order-utils/src/asset_data_utils.ts +++ b/packages/order-utils/src/asset_data_utils.ts @@ -387,6 +387,9 @@ export const assetDataUtils = { case AssetProxyId.MultiAsset: assetDataUtils.assertIsMultiAssetData(assetData); break; + case AssetProxyId.StaticCall: + assetDataUtils.assertIsStaticCallAssetData(assetData); + break; default: throw new Error(`Unrecognized asset proxy id: ${assetProxyId}`); }