From c5f8b9c2d2b1eed5789b40c4df07e98afe0431ab Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Thu, 23 Aug 2018 17:25:22 -0700 Subject: Add pragma experimental v0.5.0 to SignatureValidator and add tests --- packages/contracts/compiler.json | 1 + packages/contracts/package.json | 3 +- .../protocol/Exchange/MixinSignatureValidator.sol | 1 + .../TestSignatureValidator.sol | 1 + .../2.0.0/test/TestStaticCall/TestStaticCall.sol | 64 ++++++++++++++++++++++ .../contracts/test/exchange/signature_validator.ts | 53 +++++++++++++++++- packages/contracts/test/utils/artifacts.ts | 2 + 7 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 packages/contracts/src/2.0.0/test/TestStaticCall/TestStaticCall.sol (limited to 'packages') diff --git a/packages/contracts/compiler.json b/packages/contracts/compiler.json index 16524231c..741614f2c 100644 --- a/packages/contracts/compiler.json +++ b/packages/contracts/compiler.json @@ -47,6 +47,7 @@ "TestLibs", "TestExchangeInternals", "TestSignatureValidator", + "TestStaticCall", "TokenRegistry", "Validator", "Wallet", diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 10ab09767..6fd5a864b 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -33,7 +33,8 @@ "lint-contracts": "solhint src/2.0.0/**/**/**/**/*.sol" }, "config": { - "abis": "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json" + "abis": + "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCall|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json" }, "repository": { "type": "git", diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol index 44de54817..fd655e757 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol @@ -17,6 +17,7 @@ */ pragma solidity 0.4.24; +pragma experimental "v0.5.0"; import "../../utils/LibBytes/LibBytes.sol"; import "./mixins/MSignatureValidator.sol"; diff --git a/packages/contracts/src/2.0.0/test/TestSignatureValidator/TestSignatureValidator.sol b/packages/contracts/src/2.0.0/test/TestSignatureValidator/TestSignatureValidator.sol index e1a610469..3845b7b9e 100644 --- a/packages/contracts/src/2.0.0/test/TestSignatureValidator/TestSignatureValidator.sol +++ b/packages/contracts/src/2.0.0/test/TestSignatureValidator/TestSignatureValidator.sol @@ -17,6 +17,7 @@ */ pragma solidity 0.4.24; +pragma experimental "v0.5.0"; import "../../protocol/Exchange/MixinSignatureValidator.sol"; import "../../protocol/Exchange/MixinTransactions.sol"; diff --git a/packages/contracts/src/2.0.0/test/TestStaticCall/TestStaticCall.sol b/packages/contracts/src/2.0.0/test/TestStaticCall/TestStaticCall.sol new file mode 100644 index 000000000..be24e2f37 --- /dev/null +++ b/packages/contracts/src/2.0.0/test/TestStaticCall/TestStaticCall.sol @@ -0,0 +1,64 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + + +contract TestStaticCall { + + uint256 internal state = 1; + + /// @dev Updates state and returns true. Intended to be used with `Validator` signature type. + /// @param hash Message hash that is signed. + /// @param signerAddress Address that should have signed the given hash. + /// @param signature Proof of signing. + /// @return Validity of order signature. + function isValidSignature( + bytes32 hash, + address signerAddress, + bytes signature + ) + external + returns (bool isValid) + { + updateState(); + return true; + } + + /// @dev Updates state and returns true. Intended to be used with `Wallet` signature type. + /// @param hash Message hash that is signed. + /// @param signature Proof of signing. + /// @return Validity of order signature. + function isValidSignature( + bytes32 hash, + bytes signature + ) + external + returns (bool isValid) + { + updateState(); + return true; + } + + /// @dev Increments state variable. + function updateState() + internal + { + state++; + } +} diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts index cd4f3d6f3..0e2967bc7 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -9,11 +9,12 @@ import { TestSignatureValidatorContract, TestSignatureValidatorSignatureValidatorApprovalEventArgs, } from '../../generated_contract_wrappers/test_signature_validator'; +import { TestStaticCallContract } from '../../generated_contract_wrappers/test_static_call'; import { ValidatorContract } from '../../generated_contract_wrappers/validator'; import { WalletContract } from '../../generated_contract_wrappers/wallet'; import { addressUtils } from '../utils/address_utils'; import { artifacts } from '../utils/artifacts'; -import { expectContractCallFailed } from '../utils/assertions'; +import { expectContractCallFailed, expectContractCallFailedWithoutReasonAsync } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { LogDecoder } from '../utils/log_decoder'; @@ -31,6 +32,8 @@ describe('MixinSignatureValidator', () => { let signatureValidator: TestSignatureValidatorContract; let testWallet: WalletContract; let testValidator: ValidatorContract; + let maliciousWallet: TestStaticCallContract; + let maliciousValidator: TestStaticCallContract; let signerAddress: string; let signerPrivateKey: Buffer; let notSignerAddress: string; @@ -65,6 +68,11 @@ describe('MixinSignatureValidator', () => { txDefaults, signerAddress, ); + maliciousWallet = maliciousValidator = await TestStaticCallContract.deployFrom0xArtifactAsync( + artifacts.TestStaticCall, + provider, + txDefaults, + ); signatureValidatorLogDecoder = new LogDecoder(web3Wrapper); await web3Wrapper.awaitTransactionSuccessAsync( await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync(testValidator.address, true, { @@ -72,6 +80,16 @@ describe('MixinSignatureValidator', () => { }), constants.AWAIT_TRANSACTION_MINED_MS, ); + await web3Wrapper.awaitTransactionSuccessAsync( + await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync( + maliciousValidator.address, + true, + { + from: signerAddress, + }, + ), + constants.AWAIT_TRANSACTION_MINED_MS, + ); const defaultOrderParams = { ...constants.STATIC_ORDER_PARAMS, @@ -334,6 +352,29 @@ describe('MixinSignatureValidator', () => { expect(isValidSignature).to.be.false(); }); + it('should not allow `isValidSignature` to update state when SignatureType=Wallet', async () => { + // Create EIP712 signature + const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); + const orderHashBuffer = ethUtil.toBuffer(orderHashHex); + const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey); + // Create 0x signature from EIP712 signature + const signature = Buffer.concat([ + ethUtil.toBuffer(ecSignature.v), + ecSignature.r, + ecSignature.s, + ethUtil.toBuffer(`0x${SignatureType.Wallet}`), + ]); + const signatureHex = ethUtil.bufferToHex(signature); + // Validate signature + await expectContractCallFailedWithoutReasonAsync( + signatureValidator.publicIsValidSignature.callAsync( + orderHashHex, + maliciousWallet.address, + signatureHex, + ), + ); + }); + it('should return true when SignatureType=Validator, signature is valid and validator is approved', async () => { const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`); const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`); @@ -364,6 +405,16 @@ describe('MixinSignatureValidator', () => { expect(isValidSignature).to.be.false(); }); + it('should not allow `isValidSignature` to update state when SignatureType=Validator', async () => { + const validatorAddress = ethUtil.toBuffer(`${maliciousValidator.address}`); + const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`); + const signature = Buffer.concat([validatorAddress, signatureType]); + const signatureHex = ethUtil.bufferToHex(signature); + const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); + await expectContractCallFailedWithoutReasonAsync( + signatureValidator.publicIsValidSignature.callAsync(orderHashHex, signerAddress, signatureHex), + ); + }); it('should return false when SignatureType=Validator, signature is valid and validator is not approved', async () => { // Set approval of signature validator to false await web3Wrapper.awaitTransactionSuccessAsync( diff --git a/packages/contracts/test/utils/artifacts.ts b/packages/contracts/test/utils/artifacts.ts index e8a7585ac..1c922a3e4 100644 --- a/packages/contracts/test/utils/artifacts.ts +++ b/packages/contracts/test/utils/artifacts.ts @@ -23,6 +23,7 @@ import * as TestExchangeInternals from '../../artifacts/TestExchangeInternals.js import * as TestLibBytes from '../../artifacts/TestLibBytes.json'; import * as TestLibs from '../../artifacts/TestLibs.json'; import * as TestSignatureValidator from '../../artifacts/TestSignatureValidator.json'; +import * as TestStaticCall from '../../artifacts/TestStaticCall.json'; import * as TokenRegistry from '../../artifacts/TokenRegistry.json'; import * as Validator from '../../artifacts/Validator.json'; import * as Wallet from '../../artifacts/Wallet.json'; @@ -55,6 +56,7 @@ export const artifacts = { TestLibs: (TestLibs as any) as ContractArtifact, TestExchangeInternals: (TestExchangeInternals as any) as ContractArtifact, TestSignatureValidator: (TestSignatureValidator as any) as ContractArtifact, + TestStaticCall: (TestStaticCall as any) as ContractArtifact, Validator: (Validator as any) as ContractArtifact, Wallet: (Wallet as any) as ContractArtifact, TokenRegistry: (TokenRegistry as any) as ContractArtifact, -- cgit From 0a1ae2c31139e446b2fb3b7f03caf371c6668ae2 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 24 Aug 2018 00:19:06 -0700 Subject: Remove pragma experimental v0.5.0 and use staticcall is assembly --- .../protocol/Exchange/MixinSignatureValidator.sol | 84 +++++++++++++++++++++- .../Exchange/mixins/MSignatureValidator.sol | 31 ++++++++ .../TestSignatureValidator.sol | 1 - .../2.0.0/test/TestStaticCall/TestStaticCall.sol | 16 +++++ packages/contracts/test/exchange/core.ts | 59 ++++++++++++++- .../contracts/test/exchange/signature_validator.ts | 22 +++--- 6 files changed, 198 insertions(+), 15 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol index fd655e757..6bfbb5107 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol @@ -17,7 +17,6 @@ */ pragma solidity 0.4.24; -pragma experimental "v0.5.0"; import "../../utils/LibBytes/LibBytes.sol"; import "./mixins/MSignatureValidator.sol"; @@ -192,7 +191,11 @@ contract MixinSignatureValidator is // Signature verified by wallet contract. // If used with an order, the maker of the order is the wallet contract. } else if (signatureType == SignatureType.Wallet) { - isValid = IWallet(signerAddress).isValidSignature(hash, signature); + isValid = isValidWalletSignature( + hash, + signerAddress, + signature + ); return isValid; // Signature verified by validator contract. @@ -210,7 +213,8 @@ contract MixinSignatureValidator is if (!allowedValidators[signerAddress][validatorAddress]) { return false; } - isValid = IValidator(validatorAddress).isValidSignature( + isValid = isValidValidatorSignature( + validatorAddress, hash, signerAddress, signature @@ -258,4 +262,78 @@ contract MixinSignatureValidator is // signature was invalid.) revert("SIGNATURE_UNSUPPORTED"); } + + /// @dev Verifies signature using logic defined by Wallet contract. + /// @param hash Any 32 byte hash. + /// @param walletAddress Address that should have signed the given hash + /// and defines its own signature verification method. + /// @param signature Proof that the hash has been signed by signer. + /// @return True if signature is valid for given wallet.. + function isValidWalletSignature( + bytes32 hash, + address walletAddress, + bytes signature + ) + internal + view + returns (bool isValid) + { + bytes memory calldata = abi.encodeWithSelector( + IWallet(walletAddress).isValidSignature.selector, + hash, + signature + ); + assembly { + let cdStart := add(calldata, 32) + let success := staticcall( + gas, // forward all gas + walletAddress, // address of Wallet contract + cdStart, // pointer to start of input + mload(calldata), // length of input + cdStart, // write input over output + 32 // output size is 32 bytes + ) + // Signature is valid if call did not revert and returned true + isValid := and(success, mload(cdStart)) + } + return isValid; + } + + /// @dev Verifies signature using logic defined by Validator contract. + /// @param validatorAddress Address of validator contract. + /// @param hash Any 32 byte hash. + /// @param signerAddress Address that should have signed the given hash. + /// @param signature Proof that the hash has been signed by signer. + /// @return True if the address recovered from the provided signature matches the input signer address. + function isValidValidatorSignature( + address validatorAddress, + bytes32 hash, + address signerAddress, + bytes signature + ) + internal + view + returns (bool isValid) + { + bytes memory calldata = abi.encodeWithSelector( + IValidator(signerAddress).isValidSignature.selector, + hash, + signerAddress, + signature + ); + assembly { + let cdStart := add(calldata, 32) + let success := staticcall( + gas, // forward all gas + validatorAddress, // address of Validator contract + cdStart, // pointer to start of input + mload(calldata), // length of input + cdStart, // write input over output + 32 // output size is 32 bytes + ) + // Signature is valid if call did not revert and returned true + isValid := and(success, mload(cdStart)) + } + return isValid; + } } diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol index f14f2ba00..75fe9ec46 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol @@ -43,4 +43,35 @@ contract MSignatureValidator is Trezor, // 0x08 NSignatureTypes // 0x09, number of signature types. Always leave at end. } + + /// @dev Verifies signature using logic defined by Wallet contract. + /// @param hash Any 32 byte hash. + /// @param walletAddress Address that should have signed the given hash + /// and defines its own signature verification method. + /// @param signature Proof that the hash has been signed by signer. + /// @return True if the address recovered from the provided signature matches the input signer address. + function isValidWalletSignature( + bytes32 hash, + address walletAddress, + bytes signature + ) + internal + view + returns (bool isValid); + + /// @dev Verifies signature using logic defined by Validator contract. + /// @param validatorAddress Address of validator contract. + /// @param hash Any 32 byte hash. + /// @param signerAddress Address that should have signed the given hash. + /// @param signature Proof that the hash has been signed by signer. + /// @return True if the address recovered from the provided signature matches the input signer address. + function isValidValidatorSignature( + address validatorAddress, + bytes32 hash, + address signerAddress, + bytes signature + ) + internal + view + returns (bool isValid); } diff --git a/packages/contracts/src/2.0.0/test/TestSignatureValidator/TestSignatureValidator.sol b/packages/contracts/src/2.0.0/test/TestSignatureValidator/TestSignatureValidator.sol index 3845b7b9e..e1a610469 100644 --- a/packages/contracts/src/2.0.0/test/TestSignatureValidator/TestSignatureValidator.sol +++ b/packages/contracts/src/2.0.0/test/TestSignatureValidator/TestSignatureValidator.sol @@ -17,7 +17,6 @@ */ pragma solidity 0.4.24; -pragma experimental "v0.5.0"; import "../../protocol/Exchange/MixinSignatureValidator.sol"; import "../../protocol/Exchange/MixinTransactions.sol"; diff --git a/packages/contracts/src/2.0.0/test/TestStaticCall/TestStaticCall.sol b/packages/contracts/src/2.0.0/test/TestStaticCall/TestStaticCall.sol index be24e2f37..5a9581f51 100644 --- a/packages/contracts/src/2.0.0/test/TestStaticCall/TestStaticCall.sol +++ b/packages/contracts/src/2.0.0/test/TestStaticCall/TestStaticCall.sol @@ -18,6 +18,8 @@ pragma solidity 0.4.24; +import "../../tokens/ERC20Token/IERC20Token.sol"; + contract TestStaticCall { @@ -55,6 +57,20 @@ contract TestStaticCall { return true; } + /// @dev Approves an ERC20 token to spend tokens from this address. + /// @param token Address of ERC20 token. + /// @param spender Address that will spend tokens. + /// @param value Amount of tokens spender is approved to spend. + function approveERC20( + address token, + address spender, + uint256 value + ) + external + { + IERC20Token(token).approve(spender, value); + } + /// @dev Increments state variable. function updateState() internal diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index bc2bad749..d3f9b95af 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -1,6 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { assetDataUtils, orderHashUtils } from '@0xproject/order-utils'; -import { RevertReason, SignedOrder } from '@0xproject/types'; +import { RevertReason, SignatureType, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; @@ -14,6 +14,7 @@ import { DummyNoReturnERC20TokenContract } from '../../generated_contract_wrappe import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy'; import { ExchangeCancelEventArgs, ExchangeContract } from '../../generated_contract_wrappers/exchange'; +import { TestStaticCallContract } from '../../generated_contract_wrappers/test_static_call'; import { artifacts } from '../utils/artifacts'; import { expectTransactionFailedAsync } from '../utils/assertions'; import { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from '../utils/block_timestamp'; @@ -44,6 +45,8 @@ describe('Exchange core', () => { let exchange: ExchangeContract; let erc20Proxy: ERC20ProxyContract; let erc721Proxy: ERC721ProxyContract; + let maliciousWallet: TestStaticCallContract; + let maliciousValidator: TestStaticCallContract; let signedOrder: SignedOrder; let erc20Balances: ERC20BalancesByOwner; @@ -109,6 +112,12 @@ describe('Exchange core', () => { constants.AWAIT_TRANSACTION_MINED_MS, ); + maliciousWallet = maliciousValidator = await TestStaticCallContract.deployFrom0xArtifactAsync( + artifacts.TestStaticCall, + provider, + txDefaults, + ); + defaultMakerAssetAddress = erc20TokenA.address; defaultTakerAssetAddress = erc20TokenB.address; @@ -161,6 +170,54 @@ describe('Exchange core', () => { RevertReason.OrderUnfillable, ); }); + + 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 web3Wrapper.awaitTransactionSuccessAsync( + await maliciousWallet.approveERC20.sendTransactionAsync( + erc20TokenA.address, + erc20Proxy.address, + constants.INITIAL_ERC20_ALLOWANCE, + ), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + signedOrder = await orderFactory.newSignedOrderAsync({ + makerAddress: maliciousMakerAddress, + makerFee: constants.ZERO_AMOUNT, + }); + signedOrder.signature = `0x0${SignatureType.Wallet}`; + await expectTransactionFailedAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.InvalidOrderSignature, + ); + }); + + 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, + ); + signedOrder = await orderFactory.newSignedOrderAsync({ + makerAddress: maliciousValidator.address, + }); + signedOrder.signature = `${maliciousValidator.address}0${SignatureType.Validator}`; + await expectTransactionFailedAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.InvalidOrderSignature, + ); + }); }); describe('Testing exchange of ERC20 tokens with no return values', () => { diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts index 0e2967bc7..75656d992 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -352,7 +352,7 @@ describe('MixinSignatureValidator', () => { expect(isValidSignature).to.be.false(); }); - it('should not allow `isValidSignature` to update state when SignatureType=Wallet', async () => { + it('should return false when `isValidSignature` attempts to update state and not allow state to be updated when SignatureType=Wallet', async () => { // Create EIP712 signature const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); const orderHashBuffer = ethUtil.toBuffer(orderHashHex); @@ -366,13 +366,12 @@ describe('MixinSignatureValidator', () => { ]); const signatureHex = ethUtil.bufferToHex(signature); // Validate signature - await expectContractCallFailedWithoutReasonAsync( - signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - maliciousWallet.address, - signatureHex, - ), + const isValid = await signatureValidator.publicIsValidSignature.callAsync( + orderHashHex, + maliciousWallet.address, + signatureHex, ); + expect(isValid).to.be.equal(false); }); it('should return true when SignatureType=Validator, signature is valid and validator is approved', async () => { @@ -405,15 +404,18 @@ describe('MixinSignatureValidator', () => { expect(isValidSignature).to.be.false(); }); - it('should not allow `isValidSignature` to update state when SignatureType=Validator', async () => { + it('should return false when `isValidSignature` attempts to update state and not allow state to be updated when SignatureType=Validator', async () => { const validatorAddress = ethUtil.toBuffer(`${maliciousValidator.address}`); const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`); const signature = Buffer.concat([validatorAddress, signatureType]); const signatureHex = ethUtil.bufferToHex(signature); const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - await expectContractCallFailedWithoutReasonAsync( - signatureValidator.publicIsValidSignature.callAsync(orderHashHex, signerAddress, signatureHex), + const isValid = await signatureValidator.publicIsValidSignature.callAsync( + orderHashHex, + maliciousValidator.address, + signatureHex, ); + expect(isValid).to.be.equal(false); }); it('should return false when SignatureType=Validator, signature is valid and validator is not approved', async () => { // Set approval of signature validator to false -- cgit From 681ed822eca91085300e85e136ddcf4abd90495c Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 24 Aug 2018 09:24:33 -0700 Subject: Revert if undefined function called in AssetProxies --- .../src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol | 3 +++ .../src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol | 3 +++ packages/contracts/test/asset_proxy/proxies.ts | 24 +++++++++++++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol index b5cec6b64..004c3892d 100644 --- a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol +++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol @@ -118,6 +118,9 @@ contract ERC20Proxy is mstore(96, 0) revert(0, 100) } + + // Revert if undefined function is called + revert(0, 0) } } diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol index 6a70c9f60..9d0bc0f74 100644 --- a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol +++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol @@ -152,6 +152,9 @@ contract ERC721Proxy is mstore(96, 0) revert(0, 100) } + + // Revert if undefined function is called + revert(0, 0) } } diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts index 76a020222..6031e554d 100644 --- a/packages/contracts/test/asset_proxy/proxies.ts +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -12,7 +12,7 @@ import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_prox import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy'; import { IAssetProxyContract } from '../../generated_contract_wrappers/i_asset_proxy'; import { artifacts } from '../utils/artifacts'; -import { expectTransactionFailedAsync } from '../utils/assertions'; +import { expectTransactionFailedAsync, expectTransactionFailedWithoutReasonAsync } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; @@ -99,6 +99,17 @@ describe('Asset Transfer Proxies', () => { await blockchainLifecycle.revertAsync(); }); describe('Transfer Proxy - ERC20', () => { + it('should revert if undefined function is called', async () => { + const undefinedSelector = '0x01020304'; + await expectTransactionFailedWithoutReasonAsync( + web3Wrapper.sendTransactionAsync({ + from: owner, + to: erc20Proxy.address, + value: constants.ZERO_AMOUNT, + data: undefinedSelector, + }), + ); + }); describe('transferFrom', () => { it('should successfully transfer tokens', async () => { // Construct ERC20 asset data @@ -219,6 +230,17 @@ describe('Asset Transfer Proxies', () => { }); describe('Transfer Proxy - ERC721', () => { + it('should revert if undefined function is called', async () => { + const undefinedSelector = '0x01020304'; + await expectTransactionFailedWithoutReasonAsync( + web3Wrapper.sendTransactionAsync({ + from: owner, + to: erc721Proxy.address, + value: constants.ZERO_AMOUNT, + data: undefinedSelector, + }), + ); + }); describe('transferFrom', () => { it('should successfully transfer tokens', async () => { // Construct ERC721 asset data -- cgit From 070eff6f3a2f3775ef72248c3f345c05cd833798 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 24 Aug 2018 09:27:29 -0700 Subject: Rename TestStaticCall => TestStaticCallReceiver --- packages/contracts/compiler.json | 2 +- packages/contracts/package.json | 2 +- .../2.0.0/test/TestStaticCall/TestStaticCall.sol | 80 --------------------- .../TestStaticCallReceiver.sol | 81 ++++++++++++++++++++++ packages/contracts/test/exchange/core.ts | 10 +-- .../contracts/test/exchange/signature_validator.ts | 10 +-- packages/contracts/test/utils/artifacts.ts | 4 +- 7 files changed, 95 insertions(+), 94 deletions(-) delete mode 100644 packages/contracts/src/2.0.0/test/TestStaticCall/TestStaticCall.sol create mode 100644 packages/contracts/src/2.0.0/test/TestStaticCallReceiver/TestStaticCallReceiver.sol (limited to 'packages') diff --git a/packages/contracts/compiler.json b/packages/contracts/compiler.json index 741614f2c..27bb69a36 100644 --- a/packages/contracts/compiler.json +++ b/packages/contracts/compiler.json @@ -47,7 +47,7 @@ "TestLibs", "TestExchangeInternals", "TestSignatureValidator", - "TestStaticCall", + "TestStaticCallReceiver", "TokenRegistry", "Validator", "Wallet", diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 6fd5a864b..0ead2f43f 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -34,7 +34,7 @@ }, "config": { "abis": - "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCall|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json" + "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json" }, "repository": { "type": "git", diff --git a/packages/contracts/src/2.0.0/test/TestStaticCall/TestStaticCall.sol b/packages/contracts/src/2.0.0/test/TestStaticCall/TestStaticCall.sol deleted file mode 100644 index 5a9581f51..000000000 --- a/packages/contracts/src/2.0.0/test/TestStaticCall/TestStaticCall.sol +++ /dev/null @@ -1,80 +0,0 @@ -/* - - Copyright 2018 ZeroEx Intl. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pragma solidity 0.4.24; - -import "../../tokens/ERC20Token/IERC20Token.sol"; - - -contract TestStaticCall { - - uint256 internal state = 1; - - /// @dev Updates state and returns true. Intended to be used with `Validator` signature type. - /// @param hash Message hash that is signed. - /// @param signerAddress Address that should have signed the given hash. - /// @param signature Proof of signing. - /// @return Validity of order signature. - function isValidSignature( - bytes32 hash, - address signerAddress, - bytes signature - ) - external - returns (bool isValid) - { - updateState(); - return true; - } - - /// @dev Updates state and returns true. Intended to be used with `Wallet` signature type. - /// @param hash Message hash that is signed. - /// @param signature Proof of signing. - /// @return Validity of order signature. - function isValidSignature( - bytes32 hash, - bytes signature - ) - external - returns (bool isValid) - { - updateState(); - return true; - } - - /// @dev Approves an ERC20 token to spend tokens from this address. - /// @param token Address of ERC20 token. - /// @param spender Address that will spend tokens. - /// @param value Amount of tokens spender is approved to spend. - function approveERC20( - address token, - address spender, - uint256 value - ) - external - { - IERC20Token(token).approve(spender, value); - } - - /// @dev Increments state variable. - function updateState() - internal - { - state++; - } -} diff --git a/packages/contracts/src/2.0.0/test/TestStaticCallReceiver/TestStaticCallReceiver.sol b/packages/contracts/src/2.0.0/test/TestStaticCallReceiver/TestStaticCallReceiver.sol new file mode 100644 index 000000000..41aab01c8 --- /dev/null +++ b/packages/contracts/src/2.0.0/test/TestStaticCallReceiver/TestStaticCallReceiver.sol @@ -0,0 +1,81 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "../../tokens/ERC20Token/IERC20Token.sol"; + + +// solhint-disable no-unused-vars +contract TestStaticCallReceiver { + + uint256 internal state = 1; + + /// @dev Updates state and returns true. Intended to be used with `Validator` signature type. + /// @param hash Message hash that is signed. + /// @param signerAddress Address that should have signed the given hash. + /// @param signature Proof of signing. + /// @return Validity of order signature. + function isValidSignature( + bytes32 hash, + address signerAddress, + bytes signature + ) + external + returns (bool isValid) + { + updateState(); + return true; + } + + /// @dev Updates state and returns true. Intended to be used with `Wallet` signature type. + /// @param hash Message hash that is signed. + /// @param signature Proof of signing. + /// @return Validity of order signature. + function isValidSignature( + bytes32 hash, + bytes signature + ) + external + returns (bool isValid) + { + updateState(); + return true; + } + + /// @dev Approves an ERC20 token to spend tokens from this address. + /// @param token Address of ERC20 token. + /// @param spender Address that will spend tokens. + /// @param value Amount of tokens spender is approved to spend. + function approveERC20( + address token, + address spender, + uint256 value + ) + external + { + IERC20Token(token).approve(spender, value); + } + + /// @dev Increments state variable. + function updateState() + internal + { + state++; + } +} diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index d3f9b95af..fdfb40155 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -14,7 +14,7 @@ import { DummyNoReturnERC20TokenContract } from '../../generated_contract_wrappe import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy'; import { ExchangeCancelEventArgs, ExchangeContract } from '../../generated_contract_wrappers/exchange'; -import { TestStaticCallContract } from '../../generated_contract_wrappers/test_static_call'; +import { TestStaticCallReceiverContract } from '../../generated_contract_wrappers/test_static_call_receiver'; import { artifacts } from '../utils/artifacts'; import { expectTransactionFailedAsync } from '../utils/assertions'; import { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from '../utils/block_timestamp'; @@ -45,8 +45,8 @@ describe('Exchange core', () => { let exchange: ExchangeContract; let erc20Proxy: ERC20ProxyContract; let erc721Proxy: ERC721ProxyContract; - let maliciousWallet: TestStaticCallContract; - let maliciousValidator: TestStaticCallContract; + let maliciousWallet: TestStaticCallReceiverContract; + let maliciousValidator: TestStaticCallReceiverContract; let signedOrder: SignedOrder; let erc20Balances: ERC20BalancesByOwner; @@ -112,8 +112,8 @@ describe('Exchange core', () => { constants.AWAIT_TRANSACTION_MINED_MS, ); - maliciousWallet = maliciousValidator = await TestStaticCallContract.deployFrom0xArtifactAsync( - artifacts.TestStaticCall, + maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync( + artifacts.TestStaticCallReceiver, provider, txDefaults, ); diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts index 75656d992..947ebffcc 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -9,7 +9,7 @@ import { TestSignatureValidatorContract, TestSignatureValidatorSignatureValidatorApprovalEventArgs, } from '../../generated_contract_wrappers/test_signature_validator'; -import { TestStaticCallContract } from '../../generated_contract_wrappers/test_static_call'; +import { TestStaticCallReceiverContract } from '../../generated_contract_wrappers/test_static_call_receiver'; import { ValidatorContract } from '../../generated_contract_wrappers/validator'; import { WalletContract } from '../../generated_contract_wrappers/wallet'; import { addressUtils } from '../utils/address_utils'; @@ -32,8 +32,8 @@ describe('MixinSignatureValidator', () => { let signatureValidator: TestSignatureValidatorContract; let testWallet: WalletContract; let testValidator: ValidatorContract; - let maliciousWallet: TestStaticCallContract; - let maliciousValidator: TestStaticCallContract; + let maliciousWallet: TestStaticCallReceiverContract; + let maliciousValidator: TestStaticCallReceiverContract; let signerAddress: string; let signerPrivateKey: Buffer; let notSignerAddress: string; @@ -68,8 +68,8 @@ describe('MixinSignatureValidator', () => { txDefaults, signerAddress, ); - maliciousWallet = maliciousValidator = await TestStaticCallContract.deployFrom0xArtifactAsync( - artifacts.TestStaticCall, + maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync( + artifacts.TestStaticCallReceiver, provider, txDefaults, ); diff --git a/packages/contracts/test/utils/artifacts.ts b/packages/contracts/test/utils/artifacts.ts index 1c922a3e4..2f6fcef71 100644 --- a/packages/contracts/test/utils/artifacts.ts +++ b/packages/contracts/test/utils/artifacts.ts @@ -23,7 +23,7 @@ import * as TestExchangeInternals from '../../artifacts/TestExchangeInternals.js import * as TestLibBytes from '../../artifacts/TestLibBytes.json'; import * as TestLibs from '../../artifacts/TestLibs.json'; import * as TestSignatureValidator from '../../artifacts/TestSignatureValidator.json'; -import * as TestStaticCall from '../../artifacts/TestStaticCall.json'; +import * as TestStaticCallReceiver from '../../artifacts/TestStaticCallReceiver.json'; import * as TokenRegistry from '../../artifacts/TokenRegistry.json'; import * as Validator from '../../artifacts/Validator.json'; import * as Wallet from '../../artifacts/Wallet.json'; @@ -56,7 +56,7 @@ export const artifacts = { TestLibs: (TestLibs as any) as ContractArtifact, TestExchangeInternals: (TestExchangeInternals as any) as ContractArtifact, TestSignatureValidator: (TestSignatureValidator as any) as ContractArtifact, - TestStaticCall: (TestStaticCall as any) as ContractArtifact, + TestStaticCallReceiver: (TestStaticCallReceiver as any) as ContractArtifact, Validator: (Validator as any) as ContractArtifact, Wallet: (Wallet as any) as ContractArtifact, TokenRegistry: (TokenRegistry as any) as ContractArtifact, -- cgit From 92497d7df4b61ee62acfdd58bfb98569ff67214e Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Thu, 23 Aug 2018 11:01:52 -0700 Subject: Fix isRoundingError --- .../src/2.0.0/protocol/Exchange/libs/LibMath.sol | 27 ++++++++++++++-------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol index fa09da6ac..146bb9943 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol @@ -46,7 +46,7 @@ contract LibMath is return partialAmount; } - /// @dev Checks if rounding error > 0.1%. + /// @dev Checks if rounding error >= 0.1%. /// @param numerator Numerator. /// @param denominator Denominator. /// @param target Value to multiply with numerator/denominator. @@ -60,16 +60,23 @@ contract LibMath is pure returns (bool isError) { + // The absolute rounding error is the difference between the rounded + // value and the ideal value. The relative rounding error is the + // absolute rounding error divided by the absolute value of the + // ideal value. We want the relative rounding error to be strictly less + // than 0.1%. + // Let's call `numerator * target % denominator` the remainder. + // The ideal value is `numerator * target / denominator`. + // The absolute error is `remainder / denominator`. + // The relative error is `remainder / numerator * target`. + // We want the relative error less than 1 / 1000: + // remainder / numerator * denominator < 1 / 1000 + // or equivalently: + // 1000 * remainder < numerator * target + // so we have a rounding error iff: + // 1000 * remainder >= numerator * target uint256 remainder = mulmod(target, numerator, denominator); - if (remainder == 0) { - return false; // No rounding error. - } - - uint256 errPercentageTimes1000000 = safeDiv( - safeMul(remainder, 1000000), - safeMul(numerator, target) - ); - isError = errPercentageTimes1000000 > 1000; + isError = safeMul(1000, remainder) >= safeMul(numerator, target); return isError; } } -- cgit From 4159e45aff14c04f2d799ec8a7b6e7f1a4894064 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Thu, 23 Aug 2018 12:54:13 -0700 Subject: Update tests --- packages/contracts/test/exchange/internal.ts | 26 +++++++++++++++----------- packages/contracts/test/exchange/libs.ts | 4 ++-- packages/types/src/index.ts | 1 + 3 files changed, 18 insertions(+), 13 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/internal.ts b/packages/contracts/test/exchange/internal.ts index 67d1d2d2c..0231cc3f1 100644 --- a/packages/contracts/test/exchange/internal.ts +++ b/packages/contracts/test/exchange/internal.ts @@ -63,6 +63,7 @@ describe('Exchange core internal functions', () => { let testExchange: TestExchangeInternalsContract; let invalidOpcodeErrorForCall: Error | undefined; let overflowErrorForSendTransaction: Error | undefined; + let divisionByZeroErrorForCall: Error | undefined; before(async () => { await blockchainLifecycle.startAsync(); @@ -79,6 +80,9 @@ describe('Exchange core internal functions', () => { overflowErrorForSendTransaction = new Error( await getRevertReasonOrErrorMessageForSendTransactionAsync(RevertReason.Uint256Overflow), ); + divisionByZeroErrorForCall = new Error( + await getRevertReasonOrErrorMessageForSendTransactionAsync(RevertReason.DivisionByZero), + ); invalidOpcodeErrorForCall = new Error(await getInvalidOpcodeErrorMessageForCallAsync()); }); // Note(albrow): Don't forget to add beforeEach and afterEach calls to reset @@ -215,26 +219,26 @@ describe('Exchange core internal functions', () => { denominator: BigNumber, target: BigNumber, ): Promise { - const product = numerator.mul(target); if (denominator.eq(0)) { - throw invalidOpcodeErrorForCall; + throw divisionByZeroErrorForCall; } - const remainder = product.mod(denominator); - if (remainder.eq(0)) { + if (numerator.eq(0)) { + return false; + } + if (target.eq(0)) { return false; } + const product = numerator.mul(target); + const remainder = product.mod(denominator); + const remainderTimes1000 = remainder.mul('1000'); + const isError = remainderTimes1000.gt(product); if (product.greaterThan(MAX_UINT256)) { throw overflowErrorForCall; } - if (product.eq(0)) { - throw invalidOpcodeErrorForCall; - } - const remainderTimes1000000 = remainder.mul('1000000'); - if (remainderTimes1000000.greaterThan(MAX_UINT256)) { + if (remainderTimes1000.greaterThan(MAX_UINT256)) { throw overflowErrorForCall; } - const errPercentageTimes1000000 = remainderTimes1000000.dividedToIntegerBy(product); - return errPercentageTimes1000000.greaterThan('1000'); + return isError; } async function testIsRoundingErrorAsync( numerator: BigNumber, diff --git a/packages/contracts/test/exchange/libs.ts b/packages/contracts/test/exchange/libs.ts index 6c3305d1d..c08d62758 100644 --- a/packages/contracts/test/exchange/libs.ts +++ b/packages/contracts/test/exchange/libs.ts @@ -71,13 +71,13 @@ describe('Exchange libs', () => { // combinatorial tests in test/exchange/internal. They test specific edge // cases that are not covered by the combinatorial tests. describe('LibMath', () => { - it('should return false if there is a rounding error of 0.1%', async () => { + it('should return true if there is a rounding error of 0.1%', async () => { const numerator = new BigNumber(20); const denominator = new BigNumber(999); const target = new BigNumber(50); // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1% const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); - expect(isRoundingError).to.be.false(); + expect(isRoundingError).to.be.true(); }); it('should return false if there is a rounding of 0.09%', async () => { const numerator = new BigNumber(20); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 298fa77d4..2353c0807 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -175,6 +175,7 @@ export enum RevertReason { InvalidSender = 'INVALID_SENDER', InvalidOrderSignature = 'INVALID_ORDER_SIGNATURE', InvalidTakerAmount = 'INVALID_TAKER_AMOUNT', + DivisionByZero = 'DIVISION_BY_ZERO', RoundingError = 'ROUNDING_ERROR', InvalidSignature = 'INVALID_SIGNATURE', SignatureIllegal = 'SIGNATURE_ILLEGAL', -- cgit From e68942ee78eb19c27a96fb0b6b8b05c83b647bcc Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Thu, 23 Aug 2018 12:54:39 -0700 Subject: Handle zero case --- .../src/2.0.0/protocol/Exchange/libs/LibMath.sol | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol index 146bb9943..758b7ec90 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol @@ -60,14 +60,26 @@ contract LibMath is pure returns (bool isError) { + require(denominator > 0, "DIVISION_BY_ZERO"); + // The absolute rounding error is the difference between the rounded // value and the ideal value. The relative rounding error is the // absolute rounding error divided by the absolute value of the - // ideal value. We want the relative rounding error to be strictly less - // than 0.1%. - // Let's call `numerator * target % denominator` the remainder. + // ideal value. This is undefined when the ideal value is zero. + // // The ideal value is `numerator * target / denominator`. + // Let's call `numerator * target % denominator` the remainder. // The absolute error is `remainder / denominator`. + // + // When the ideal value is zero, we require the absolute error to + // be zero. Fortunately, this is always the case. The ideal value is + // zero iff `numerator == 0` and/or `target == 0`. In this case the + // remainder and absolute error are also zero. + if (target == 0 || numerator == 0) { + return false; + } + // Otherwise, we want the relative rounding error to be strictly + // less than 0.1%. // The relative error is `remainder / numerator * target`. // We want the relative error less than 1 / 1000: // remainder / numerator * denominator < 1 / 1000 -- cgit From 6a9669a409a61c4645af43f39a4e4a0761354e32 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 24 Aug 2018 14:06:46 -0700 Subject: Rethrow Wallet and Validator errors --- .../protocol/Exchange/MixinSignatureValidator.sol | 32 +++++++++++++++++++--- packages/contracts/test/exchange/core.ts | 7 ++--- .../contracts/test/exchange/signature_validator.ts | 25 ++++++++--------- packages/types/CHANGELOG.json | 9 ++++++ packages/types/src/index.ts | 2 ++ 5 files changed, 53 insertions(+), 22 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol index 6bfbb5107..017da742e 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol @@ -293,8 +293,20 @@ contract MixinSignatureValidator is cdStart, // write input over output 32 // output size is 32 bytes ) - // Signature is valid if call did not revert and returned true - isValid := and(success, mload(cdStart)) + + switch success + case 0 { + // Revert with `Error("WALLET_ERROR")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000000c57414c4c45545f4552524f5200000000000000000000000000000000) + mstore(96, 0) + revert(0, 100) + } + case 1 { + // Signature is valid if call did not revert and returned true + isValid := mload(cdStart) + } } return isValid; } @@ -331,8 +343,20 @@ contract MixinSignatureValidator is cdStart, // write input over output 32 // output size is 32 bytes ) - // Signature is valid if call did not revert and returned true - isValid := and(success, mload(cdStart)) + + switch success + case 0 { + // Revert with `Error("VALIDATOR_ERROR")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000000f56414c494441544f525f4552524f5200000000000000000000000000) + mstore(96, 0) + revert(0, 100) + } + case 1 { + // Signature is valid if call did not revert and returned true + isValid := mload(cdStart) + } } return isValid; } diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index fdfb40155..f9d8b7783 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -195,7 +195,7 @@ describe('Exchange core', () => { signedOrder.signature = `0x0${SignatureType.Wallet}`; await expectTransactionFailedAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - RevertReason.InvalidOrderSignature, + RevertReason.WalletError, ); }); @@ -209,13 +209,10 @@ describe('Exchange core', () => { ), constants.AWAIT_TRANSACTION_MINED_MS, ); - signedOrder = await orderFactory.newSignedOrderAsync({ - makerAddress: maliciousValidator.address, - }); signedOrder.signature = `${maliciousValidator.address}0${SignatureType.Validator}`; await expectTransactionFailedAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - RevertReason.InvalidOrderSignature, + RevertReason.ValidatorError, ); }); }); diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts index 947ebffcc..5b64d853c 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -352,7 +352,7 @@ describe('MixinSignatureValidator', () => { expect(isValidSignature).to.be.false(); }); - it('should return false when `isValidSignature` attempts to update state and not allow state to be updated when SignatureType=Wallet', async () => { + it('should revert when `isValidSignature` attempts to update state and SignatureType=Wallet', async () => { // Create EIP712 signature const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); const orderHashBuffer = ethUtil.toBuffer(orderHashHex); @@ -365,13 +365,14 @@ describe('MixinSignatureValidator', () => { ethUtil.toBuffer(`0x${SignatureType.Wallet}`), ]); const signatureHex = ethUtil.bufferToHex(signature); - // Validate signature - const isValid = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - maliciousWallet.address, - signatureHex, + await expectContractCallFailed( + signatureValidator.publicIsValidSignature.callAsync( + orderHashHex, + maliciousWallet.address, + signatureHex, + ), + RevertReason.WalletError, ); - expect(isValid).to.be.equal(false); }); it('should return true when SignatureType=Validator, signature is valid and validator is approved', async () => { @@ -404,18 +405,16 @@ describe('MixinSignatureValidator', () => { expect(isValidSignature).to.be.false(); }); - it('should return false when `isValidSignature` attempts to update state and not allow state to be updated when SignatureType=Validator', async () => { + it('should revert when `isValidSignature` attempts to update state and SignatureType=Validator', async () => { const validatorAddress = ethUtil.toBuffer(`${maliciousValidator.address}`); const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`); const signature = Buffer.concat([validatorAddress, signatureType]); const signatureHex = ethUtil.bufferToHex(signature); const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const isValid = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - maliciousValidator.address, - signatureHex, + await expectContractCallFailed( + signatureValidator.publicIsValidSignature.callAsync(orderHashHex, signerAddress, signatureHex), + RevertReason.ValidatorError, ); - expect(isValid).to.be.equal(false); }); it('should return false when SignatureType=Validator, signature is valid and validator is not approved', async () => { // Set approval of signature validator to false diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json index fabc80ecf..980a12ad3 100644 --- a/packages/types/CHANGELOG.json +++ b/packages/types/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.0.1-rc.6", + "changes": [ + { + "note": "Add WalletError and ValidatorError revert reasons", + "pr": 1012 + } + ] + }, { "version": "1.0.1-rc.5", "changes": [ diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 298fa77d4..2831d816d 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -220,6 +220,8 @@ export enum RevertReason { Erc721InvalidSpender = 'ERC721_INVALID_SPENDER', Erc721ZeroOwner = 'ERC721_ZERO_OWNER', Erc721InvalidSelector = 'ERC721_INVALID_SELECTOR', + WalletError = 'WALLET_ERROR', + ValidatorError = 'VALIDATOR_ERROR', } export enum StatusCodes { -- cgit From ab5df342e1dc4add20223fab7128f9323a114b8e Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Wed, 22 Aug 2018 12:22:49 -0700 Subject: Add getPartialAmountCeil --- .../src/2.0.0/protocol/Exchange/libs/LibMath.sol | 35 ++++++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol index 758b7ec90..3e70d1b60 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol @@ -29,7 +29,7 @@ contract LibMath is /// @param numerator Numerator. /// @param denominator Denominator. /// @param target Value to calculate partial of. - /// @return Partial value of target. + /// @return Partial value of target rounded down. function getPartialAmount( uint256 numerator, uint256 denominator, @@ -45,8 +45,37 @@ contract LibMath is ); return partialAmount; } - - /// @dev Checks if rounding error >= 0.1%. + + /// @dev Calculates partial value given a numerator and denominator. + /// @param numerator Numerator. + /// @param denominator Denominator. + /// @param target Value to calculate partial of. + /// @return Partial value of target rounded up. + function getPartialAmountCeil( + uint256 numerator, + uint256 denominator, + uint256 target + ) + internal + pure + returns (uint256 partialAmount) + { + require( + denominator > 0, + "DIVISION_BY_ZERO" + ); + + // SafeDiv rounds down. To use it to round up we need to add + // denominator - 1. This causes anything less than an exact multiple + // of denominator to be rounded up. + partialAmount = safeDiv( + safeAdd(safeMul(numerator, target), safeSub(denominator, 1)), + denominator + ); + return partialAmount; + } + + /// @dev Checks if rounding error > 0.1%. /// @param numerator Numerator. /// @param denominator Denominator. /// @param target Value to multiply with numerator/denominator. -- cgit From 5d70df771b151800b8a04b5569529843701c9fbd Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Wed, 22 Aug 2018 12:22:58 -0700 Subject: Add isRoundingErrorCeil --- .../src/2.0.0/protocol/Exchange/libs/LibMath.sol | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol index 3e70d1b60..c4aa2abb5 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol @@ -107,6 +107,7 @@ contract LibMath is if (target == 0 || numerator == 0) { return false; } + // Otherwise, we want the relative rounding error to be strictly // less than 0.1%. // The relative error is `remainder / numerator * target`. @@ -120,4 +121,32 @@ contract LibMath is isError = safeMul(1000, remainder) >= safeMul(numerator, target); return isError; } + + /// @dev Checks if rounding error > 0.1%. + /// @param numerator Numerator. + /// @param denominator Denominator. + /// @param target Value to multiply with numerator/denominator. + /// @return Rounding error is present. + function isRoundingErrorCeil( + uint256 numerator, + uint256 denominator, + uint256 target + ) + internal + pure + returns (bool isError) + { + require(denominator > 0, "DIVISION_BY_ZERO"); + + if (target == 0 || numerator == 0) { + return false; + } + uint256 remainder = mulmod(target, numerator, denominator); + if (remainder == 0) { + return false; + } + remainder = safeSub(denominator, remainder); + isError = safeMul(1000, remainder) >= safeMul(numerator, target); + return isError; + } } -- cgit From 3dad6ee55e9f51daa66cfe3c83dd17abc31f23f5 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Wed, 22 Aug 2018 12:23:32 -0700 Subject: Add tests for getPartialAmountCeil --- .../TestExchangeInternals.sol | 17 +++++++++ packages/contracts/test/exchange/internal.ts | 40 ++++++++++++++++++++++ 2 files changed, 57 insertions(+) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol b/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol index d9cec9edc..239dd10a8 100644 --- a/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol +++ b/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol @@ -79,6 +79,23 @@ contract TestExchangeInternals is return getPartialAmount(numerator, denominator, target); } + /// @dev Calculates partial value given a numerator and denominator. + /// @param numerator Numerator. + /// @param denominator Denominator. + /// @param target Value to calculate partial of. + /// @return Partial value of target. + function publicGetPartialAmountCeil( + uint256 numerator, + uint256 denominator, + uint256 target + ) + public + pure + returns (uint256 partialAmount) + { + return getPartialAmountCeil(numerator, denominator, target); + } + /// @dev Checks if rounding error > 0.1%. /// @param numerator Numerator. /// @param denominator Denominator. diff --git a/packages/contracts/test/exchange/internal.ts b/packages/contracts/test/exchange/internal.ts index 0231cc3f1..69625ee1d 100644 --- a/packages/contracts/test/exchange/internal.ts +++ b/packages/contracts/test/exchange/internal.ts @@ -1,6 +1,7 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { Order, RevertReason, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; import * as _ from 'lodash'; import { TestExchangeInternalsContract } from '../../generated_contract_wrappers/test_exchange_internals'; @@ -16,6 +17,8 @@ import { FillResults } from '../utils/types'; import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; chaiSetup.configure(); +const expect = chai.expect; + const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); const MAX_UINT256 = new BigNumber(2).pow(256).minus(1); @@ -213,6 +216,43 @@ describe('Exchange core internal functions', () => { ); }); + describe.only('getPartialAmountCeil', async () => { + async function referenceGetPartialAmountCeilAsync( + numerator: BigNumber, + denominator: BigNumber, + target: BigNumber, + ) { + if (denominator.eq(0)) { + throw new Error('revert DIVISION_BY_ZERO'); + } + const product = numerator.mul(target); + const offset = product.add(denominator.sub(1)); + if (offset.greaterThan(MAX_UINT256)) { + throw overflowErrorForCall; + } + const result = offset.dividedToIntegerBy(denominator); + if (product.mod(denominator).eq(0)) { + expect(result.mul(denominator)).to.be.bignumber.eq(product); + } else { + expect(result.mul(denominator)).to.be.bignumber.gt(product); + } + return result; + } + async function testGetPartialAmountCeilAsync( + numerator: BigNumber, + denominator: BigNumber, + target: BigNumber, + ): Promise { + return testExchange.publicGetPartialAmountCeil.callAsync(numerator, denominator, target); + } + await testCombinatoriallyWithReferenceFuncAsync( + 'getPartialAmountCeil', + referenceGetPartialAmountCeilAsync, + testGetPartialAmountCeilAsync, + [uint256Values, uint256Values, uint256Values], + ); + }); + describe('isRoundingError', async () => { async function referenceIsRoundingErrorAsync( numerator: BigNumber, -- cgit From 50fab9feb3b80b7d5ac1e94df9af68cad4aaabe0 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Wed, 22 Aug 2018 12:22:58 -0700 Subject: Improve getPartialAmountCeil docs --- packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol index c4aa2abb5..d123c55a1 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol @@ -65,9 +65,9 @@ contract LibMath is "DIVISION_BY_ZERO" ); - // SafeDiv rounds down. To use it to round up we need to add - // denominator - 1. This causes anything less than an exact multiple - // of denominator to be rounded up. + // safeDiv computes `floor(a / b)`. We use the identity (a, b integer): + // ceil(a / b) = floor((a + b - 1) / b) + // To implement `ceil(a / b)` using safeDiv. partialAmount = safeDiv( safeAdd(safeMul(numerator, target), safeSub(denominator, 1)), denominator -- cgit From c109d1f5451c43afd92dd0fb4bebb48cba65c661 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Thu, 23 Aug 2018 13:33:05 -0700 Subject: Remove .only --- packages/contracts/test/exchange/internal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/internal.ts b/packages/contracts/test/exchange/internal.ts index 69625ee1d..2f25b1708 100644 --- a/packages/contracts/test/exchange/internal.ts +++ b/packages/contracts/test/exchange/internal.ts @@ -216,7 +216,7 @@ describe('Exchange core internal functions', () => { ); }); - describe.only('getPartialAmountCeil', async () => { + describe('getPartialAmountCeil', async () => { async function referenceGetPartialAmountCeilAsync( numerator: BigNumber, denominator: BigNumber, -- cgit From 4219af1430f1cfc105d3521616941b7947fde4e3 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Thu, 23 Aug 2018 14:22:59 -0700 Subject: Add DIVISION_BY_ZERO to getPartialAmount for consistency --- .../src/2.0.0/protocol/Exchange/libs/LibMath.sol | 7 +++-- packages/contracts/test/exchange/internal.ts | 31 +++++++++++----------- 2 files changed, 18 insertions(+), 20 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol index d123c55a1..f4e2f1958 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol @@ -39,6 +39,8 @@ contract LibMath is pure returns (uint256 partialAmount) { + require(denominator > 0, "DIVISION_BY_ZERO"); + partialAmount = safeDiv( safeMul(numerator, target), denominator @@ -60,10 +62,7 @@ contract LibMath is pure returns (uint256 partialAmount) { - require( - denominator > 0, - "DIVISION_BY_ZERO" - ); + require(denominator > 0, "DIVISION_BY_ZERO"); // safeDiv computes `floor(a / b)`. We use the identity (a, b integer): // ceil(a / b) = floor((a + b - 1) / b) diff --git a/packages/contracts/test/exchange/internal.ts b/packages/contracts/test/exchange/internal.ts index 2f25b1708..48e1e620c 100644 --- a/packages/contracts/test/exchange/internal.ts +++ b/packages/contracts/test/exchange/internal.ts @@ -46,22 +46,6 @@ const emptySignedOrder: SignedOrder = { const overflowErrorForCall = new Error(RevertReason.Uint256Overflow); -async function referenceGetPartialAmountAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, -): Promise { - const invalidOpcodeErrorForCall = new Error(await getInvalidOpcodeErrorMessageForCallAsync()); - const product = numerator.mul(target); - if (product.greaterThan(MAX_UINT256)) { - throw overflowErrorForCall; - } - if (denominator.eq(0)) { - throw invalidOpcodeErrorForCall; - } - return product.dividedToIntegerBy(denominator); -} - describe('Exchange core internal functions', () => { let testExchange: TestExchangeInternalsContract; let invalidOpcodeErrorForCall: Error | undefined; @@ -91,6 +75,21 @@ describe('Exchange core internal functions', () => { // Note(albrow): Don't forget to add beforeEach and afterEach calls to reset // the blockchain state for any tests which modify it! + async function referenceGetPartialAmountAsync( + numerator: BigNumber, + denominator: BigNumber, + target: BigNumber, + ): Promise { + if (denominator.eq(0)) { + throw divisionByZeroErrorForCall; + } + const product = numerator.mul(target); + if (product.greaterThan(MAX_UINT256)) { + throw overflowErrorForCall; + } + return product.dividedToIntegerBy(denominator); + } + describe('addFillResults', async () => { function makeFillResults(value: BigNumber): FillResults { return { -- cgit From 0fb7617a785b765415cb7f43448c9b8ea905963f Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Thu, 23 Aug 2018 15:18:45 -0700 Subject: Fix incorect modulus --- packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol index f4e2f1958..1c14dbcae 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol @@ -141,10 +141,8 @@ contract LibMath is return false; } uint256 remainder = mulmod(target, numerator, denominator); - if (remainder == 0) { - return false; - } - remainder = safeSub(denominator, remainder); + // TODO: safeMod + remainder = safeSub(denominator, remainder) % denominator; isError = safeMul(1000, remainder) >= safeMul(numerator, target); return isError; } -- cgit From 6734f2f1bcdab8f0d50524a26195707da00bd8ed Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Thu, 23 Aug 2018 15:18:55 -0700 Subject: Add docs --- packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol index 1c14dbcae..8a1444af1 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol @@ -74,7 +74,7 @@ contract LibMath is return partialAmount; } - /// @dev Checks if rounding error > 0.1%. + /// @dev Checks if rounding error >= 0.1% when rounding down. /// @param numerator Numerator. /// @param denominator Denominator. /// @param target Value to multiply with numerator/denominator. @@ -121,7 +121,7 @@ contract LibMath is return isError; } - /// @dev Checks if rounding error > 0.1%. + /// @dev Checks if rounding error >= 0.1% when rounding up. /// @param numerator Numerator. /// @param denominator Denominator. /// @param target Value to multiply with numerator/denominator. @@ -137,9 +137,14 @@ contract LibMath is { require(denominator > 0, "DIVISION_BY_ZERO"); + // See the comments in `isRoundingError`. if (target == 0 || numerator == 0) { + // When either is zero, the ideal value and rounded value are zero + // and there is no rounding error. (Although the relative error + // is undefined.) return false; } + // Compute remainder as before uint256 remainder = mulmod(target, numerator, denominator); // TODO: safeMod remainder = safeSub(denominator, remainder) % denominator; -- cgit From 7f78d7da9dd13d1f0068a292bcd1ee3c5439d5a8 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Thu, 23 Aug 2018 15:19:29 -0700 Subject: Add tests --- .../TestExchangeInternals.sol | 19 +++++- .../contracts/src/2.0.0/test/TestLibs/TestLibs.sol | 34 ++++++++++ packages/contracts/test/exchange/internal.ts | 43 +++++++++++++ packages/contracts/test/exchange/libs.ts | 72 +++++++++++++++------- 4 files changed, 145 insertions(+), 23 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol b/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol index 239dd10a8..5e2ae2f0e 100644 --- a/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol +++ b/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol @@ -96,7 +96,7 @@ contract TestExchangeInternals is return getPartialAmountCeil(numerator, denominator, target); } - /// @dev Checks if rounding error > 0.1%. + /// @dev Checks if rounding error >= 0.1%. /// @param numerator Numerator. /// @param denominator Denominator. /// @param target Value to multiply with numerator/denominator. @@ -112,6 +112,23 @@ contract TestExchangeInternals is { return isRoundingError(numerator, denominator, target); } + + /// @dev Checks if rounding error >= 0.1%. + /// @param numerator Numerator. + /// @param denominator Denominator. + /// @param target Value to multiply with numerator/denominator. + /// @return Rounding error is present. + function publicIsRoundingErrorCeil( + uint256 numerator, + uint256 denominator, + uint256 target + ) + public + pure + returns (bool isError) + { + return isRoundingErrorCeil(numerator, denominator, target); + } /// @dev Updates state with results of a fill order. /// @param order that was filled. diff --git a/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol b/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol index 4a99dd9c1..8c3d12226 100644 --- a/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol +++ b/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol @@ -66,6 +66,23 @@ contract TestLibs is return partialAmount; } + function publicGetPartialAmountCeil( + uint256 numerator, + uint256 denominator, + uint256 target + ) + public + pure + returns (uint256 partialAmount) + { + partialAmount = getPartialAmountCeil( + numerator, + denominator, + target + ); + return partialAmount; + } + function publicIsRoundingError( uint256 numerator, uint256 denominator, @@ -83,6 +100,23 @@ contract TestLibs is return isError; } + function publicIsRoundingErrorCeil( + uint256 numerator, + uint256 denominator, + uint256 target + ) + public + pure + returns (bool isError) + { + isError = isRoundingErrorCeil( + numerator, + denominator, + target + ); + return isError; + } + function publicGetOrderHash(Order memory order) public view diff --git a/packages/contracts/test/exchange/internal.ts b/packages/contracts/test/exchange/internal.ts index 48e1e620c..0c6ab3707 100644 --- a/packages/contracts/test/exchange/internal.ts +++ b/packages/contracts/test/exchange/internal.ts @@ -294,6 +294,49 @@ describe('Exchange core internal functions', () => { ); }); + describe('isRoundingErrorCeil', async () => { + async function referenceIsRoundingErrorAsync( + numerator: BigNumber, + denominator: BigNumber, + target: BigNumber, + ): Promise { + if (denominator.eq(0)) { + throw divisionByZeroErrorForCall; + } + if (numerator.eq(0)) { + return false; + } + if (target.eq(0)) { + return false; + } + const product = numerator.mul(target); + const remainder = product.mod(denominator); + const error = denominator.sub(remainder).mod(denominator); + const errorTimes1000 = error.mul('1000'); + const isError = errorTimes1000.gt(product); + if (product.greaterThan(MAX_UINT256)) { + throw overflowErrorForCall; + } + if (errorTimes1000.greaterThan(MAX_UINT256)) { + throw overflowErrorForCall; + } + return isError; + } + async function testIsRoundingErrorCeilAsync( + numerator: BigNumber, + denominator: BigNumber, + target: BigNumber, + ): Promise { + return testExchange.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target); + } + await testCombinatoriallyWithReferenceFuncAsync( + 'isRoundingErrorCeil', + referenceIsRoundingErrorAsync, + testIsRoundingErrorCeilAsync, + [uint256Values, uint256Values, uint256Values], + ); + }); + describe('updateFilledState', async () => { // Note(albrow): Since updateFilledState modifies the state by calling // sendTransaction, we must reset the state after each test. diff --git a/packages/contracts/test/exchange/libs.ts b/packages/contracts/test/exchange/libs.ts index c08d62758..a5f31a498 100644 --- a/packages/contracts/test/exchange/libs.ts +++ b/packages/contracts/test/exchange/libs.ts @@ -71,29 +71,57 @@ describe('Exchange libs', () => { // combinatorial tests in test/exchange/internal. They test specific edge // cases that are not covered by the combinatorial tests. describe('LibMath', () => { - it('should return true if there is a rounding error of 0.1%', async () => { - const numerator = new BigNumber(20); - const denominator = new BigNumber(999); - const target = new BigNumber(50); - // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1% - const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); - expect(isRoundingError).to.be.true(); - }); - it('should return false if there is a rounding of 0.09%', async () => { - const numerator = new BigNumber(20); - const denominator = new BigNumber(9991); - const target = new BigNumber(500); - // rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09% - const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); - expect(isRoundingError).to.be.false(); + describe('isRoundingError', () => { + it('should return true if there is a rounding error of 0.1%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(999); + const target = new BigNumber(50); + // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1% + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.true(); + }); + it('should return false if there is a rounding of 0.09%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(9991); + const target = new BigNumber(500); + // rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09% + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.false(); + }); + it('should return true if there is a rounding error of 0.11%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(9989); + const target = new BigNumber(500); + // rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011% + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.true(); + }); }); - it('should return true if there is a rounding error of 0.11%', async () => { - const numerator = new BigNumber(20); - const denominator = new BigNumber(9989); - const target = new BigNumber(500); - // rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011% - const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); - expect(isRoundingError).to.be.true(); + describe('isRoundingErrorCeil', () => { + it('should return true if there is a rounding error of 0.1%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(1001); + const target = new BigNumber(50); + // rounding error = (ceil(20*50/1001) - (20*50/1001)) / (20*50/1001) = 0.1% + const isRoundingError = await libs.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.true(); + }); + it('should return false if there is a rounding of 0.09%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(10009); + const target = new BigNumber(500); + // rounding error = (ceil(20*500/10009) - (20*500/10009)) / (20*500/10009) = 0.09% + const isRoundingError = await libs.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.false(); + }); + it('should return true if there is a rounding error of 0.11%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(10011); + const target = new BigNumber(500); + // rounding error = (ceil(20*500/10011) - (20*500/10011)) / (20*500/10011) = 0.11% + const isRoundingError = await libs.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.true(); + }); }); }); -- cgit From f6080367fe3a90762afea76a653c5df1315c45fa Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Fri, 24 Aug 2018 14:11:45 -0700 Subject: Disambiguate the operator precedence --- packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol index 8a1444af1..90ec9e2b6 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol @@ -109,9 +109,9 @@ contract LibMath is // Otherwise, we want the relative rounding error to be strictly // less than 0.1%. - // The relative error is `remainder / numerator * target`. + // The relative error is `remainder / (numerator * target)`. // We want the relative error less than 1 / 1000: - // remainder / numerator * denominator < 1 / 1000 + // remainder / (numerator * denominator) < 1 / 1000 // or equivalently: // 1000 * remainder < numerator * target // so we have a rounding error iff: -- cgit From 8ce4f9c784556e93bde2c58b670bf6efd5d3b7fd Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 24 Aug 2018 10:01:24 -0700 Subject: Remove SignatureType.Caller --- .../src/2.0.0/examples/Whitelist/Whitelist.sol | 2 +- .../protocol/Exchange/MixinSignatureValidator.sol | 34 +++++++--------------- .../Exchange/mixins/MSignatureValidator.sol | 11 ++++--- .../contracts/test/exchange/signature_validator.ts | 26 ----------------- packages/types/src/index.ts | 1 - 5 files changed, 16 insertions(+), 58 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/examples/Whitelist/Whitelist.sol b/packages/contracts/src/2.0.0/examples/Whitelist/Whitelist.sol index 60cac26ea..e4e25038c 100644 --- a/packages/contracts/src/2.0.0/examples/Whitelist/Whitelist.sol +++ b/packages/contracts/src/2.0.0/examples/Whitelist/Whitelist.sol @@ -37,7 +37,7 @@ contract Whitelist is bytes internal TX_ORIGIN_SIGNATURE; // solhint-enable var-name-mixedcase - byte constant internal VALIDATOR_SIGNATURE_BYTE = "\x06"; + byte constant internal VALIDATOR_SIGNATURE_BYTE = "\x05"; constructor (address _exchange) public diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol index 017da742e..401fdd377 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol @@ -48,14 +48,16 @@ contract MixinSignatureValidator is ) external { - require( - isValidSignature( - hash, - signerAddress, - signature - ), - "INVALID_SIGNATURE" - ); + if (signerAddress != msg.sender) { + require( + isValidSignature( + hash, + signerAddress, + signature + ), + "INVALID_SIGNATURE" + ); + } preSigned[hash][signerAddress] = true; } @@ -172,22 +174,6 @@ contract MixinSignatureValidator is isValid = signerAddress == recovered; return isValid; - // Implicitly signed by caller. - // The signer has initiated the call. In the case of non-contract - // accounts it means the transaction itself was signed. - // Example: let's say for a particular operation three signatures - // A, B and C are required. To submit the transaction, A and B can - // give a signature to C, who can then submit the transaction using - // `Caller` for his own signature. Or A and C can sign and B can - // submit using `Caller`. Having `Caller` allows this flexibility. - } else if (signatureType == SignatureType.Caller) { - require( - signature.length == 0, - "LENGTH_0_REQUIRED" - ); - isValid = signerAddress == msg.sender; - return isValid; - // Signature verified by wallet contract. // If used with an order, the maker of the order is the wallet contract. } else if (signatureType == SignatureType.Wallet) { diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol index 75fe9ec46..b00c5e8da 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol @@ -36,12 +36,11 @@ contract MSignatureValidator is Invalid, // 0x01 EIP712, // 0x02 EthSign, // 0x03 - Caller, // 0x04 - Wallet, // 0x05 - Validator, // 0x06 - PreSigned, // 0x07 - Trezor, // 0x08 - NSignatureTypes // 0x09, number of signature types. Always leave at end. + Wallet, // 0x04 + Validator, // 0x05 + PreSigned, // 0x06 + Trezor, // 0x07 + NSignatureTypes // 0x08, number of signature types. Always leave at end. } /// @dev Verifies signature using logic defined by Wallet contract. diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts index 5b64d853c..ac07b0636 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -281,32 +281,6 @@ describe('MixinSignatureValidator', () => { expect(isValidSignature).to.be.false(); }); - it('should return true when SignatureType=Caller and signer is caller', async () => { - const signature = ethUtil.toBuffer(`0x${SignatureType.Caller}`); - const signatureHex = ethUtil.bufferToHex(signature); - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - signerAddress, - signatureHex, - { from: signerAddress }, - ); - expect(isValidSignature).to.be.true(); - }); - - it('should return false when SignatureType=Caller and signer is not caller', async () => { - const signature = ethUtil.toBuffer(`0x${SignatureType.Caller}`); - const signatureHex = ethUtil.bufferToHex(signature); - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - signerAddress, - signatureHex, - { from: notSignerAddress }, - ); - expect(isValidSignature).to.be.false(); - }); - it('should return true when SignatureType=Wallet and signature is valid', async () => { // Create EIP712 signature const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 2831d816d..beedc24a3 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -133,7 +133,6 @@ export enum SignatureType { Invalid, EIP712, EthSign, - Caller, Wallet, Validator, PreSigned, -- cgit From 4f27991959ba6e4e15839e6195a40b0fd8a2122c Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 24 Aug 2018 11:22:17 -0700 Subject: Remove SigntureType.Caller from signingUtils --- packages/order-utils/src/signature_utils.ts | 5 ----- 1 file changed, 5 deletions(-) (limited to 'packages') diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index 40bbcef98..f8ed9cf9c 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -53,11 +53,6 @@ export const signatureUtils = { return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); } - case SignatureType.Caller: - // HACK: We currently do not "validate" the caller signature type. - // It can only be validated during Exchange contract execution. - throw new Error('Caller signature type cannot be validated off-chain'); - case SignatureType.Wallet: { const isValid = await signatureUtils.isValidWalletSignatureAsync( provider, -- cgit From 1932aff35c6b07093534cde66dec63c5f919fcf0 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 24 Aug 2018 13:11:17 -0700 Subject: Remove Trezor SignatureType --- .../protocol/Exchange/MixinSignatureValidator.sol | 28 ------------- .../Exchange/mixins/MSignatureValidator.sol | 3 +- .../contracts/test/exchange/signature_validator.ts | 47 ---------------------- packages/order-utils/CHANGELOG.json | 9 +++++ packages/order-utils/src/signature_utils.ts | 12 +----- packages/order-utils/test/signature_utils_test.ts | 14 ------- packages/types/CHANGELOG.json | 4 ++ packages/types/src/index.ts | 1 - 8 files changed, 15 insertions(+), 103 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol index 401fdd377..f30adcdb8 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol @@ -211,34 +211,6 @@ contract MixinSignatureValidator is } else if (signatureType == SignatureType.PreSigned) { isValid = preSigned[hash][signerAddress]; return isValid; - - // Signature from Trezor hardware wallet. - // It differs from web3.eth_sign in the encoding of message length - // (Bitcoin varint encoding vs ascii-decimal, the latter is not - // self-terminating which leads to ambiguities). - // See also: - // https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer - // https://github.com/trezor/trezor-mcu/blob/master/firmware/ethereum.c#L602 - // https://github.com/trezor/trezor-mcu/blob/master/firmware/crypto.c#L36 - } else if (signatureType == SignatureType.Trezor) { - require( - signature.length == 65, - "LENGTH_65_REQUIRED" - ); - v = uint8(signature[0]); - r = signature.readBytes32(1); - s = signature.readBytes32(33); - recovered = ecrecover( - keccak256(abi.encodePacked( - "\x19Ethereum Signed Message:\n\x20", - hash - )), - v, - r, - s - ); - isValid = signerAddress == recovered; - return isValid; } // Anything else is illegal (We do not return false because diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol index b00c5e8da..1fe88b908 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol @@ -39,8 +39,7 @@ contract MSignatureValidator is Wallet, // 0x04 Validator, // 0x05 PreSigned, // 0x06 - Trezor, // 0x07 - NSignatureTypes // 0x08, number of signature types. Always leave at end. + NSignatureTypes // 0x07, number of signature types. Always leave at end. } /// @dev Verifies signature using logic defined by Wallet contract. diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts index ac07b0636..9113e5248 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -414,53 +414,6 @@ describe('MixinSignatureValidator', () => { expect(isValidSignature).to.be.false(); }); - it('should return true when SignatureType=Trezor and signature is valid', async () => { - // Create Trezor signature - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const orderHashWithTrezorPrefixHex = signatureUtils.addSignedMessagePrefix(orderHashHex, SignerType.Trezor); - const orderHashWithTrezorPrefixBuffer = ethUtil.toBuffer(orderHashWithTrezorPrefixHex); - const ecSignature = ethUtil.ecsign(orderHashWithTrezorPrefixBuffer, signerPrivateKey); - // Create 0x signature from Trezor signature - const signature = Buffer.concat([ - ethUtil.toBuffer(ecSignature.v), - ecSignature.r, - ecSignature.s, - ethUtil.toBuffer(`0x${SignatureType.Trezor}`), - ]); - const signatureHex = ethUtil.bufferToHex(signature); - // Validate signature - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - signerAddress, - signatureHex, - ); - expect(isValidSignature).to.be.true(); - }); - - it('should return false when SignatureType=Trezor and signature is invalid', async () => { - // Create Trezor signature - const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); - const orderHashWithTrezorPrefixHex = signatureUtils.addSignedMessagePrefix(orderHashHex, SignerType.Trezor); - const orderHashWithTrezorPrefixBuffer = ethUtil.toBuffer(orderHashWithTrezorPrefixHex); - const ecSignature = ethUtil.ecsign(orderHashWithTrezorPrefixBuffer, signerPrivateKey); - // Create 0x signature from Trezor signature - const signature = Buffer.concat([ - ethUtil.toBuffer(ecSignature.v), - ecSignature.r, - ecSignature.s, - ethUtil.toBuffer(`0x${SignatureType.Trezor}`), - ]); - const signatureHex = ethUtil.bufferToHex(signature); - // Validate signature. - // This will fail because `signerAddress` signed the message, but we're passing in `notSignerAddress` - const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( - orderHashHex, - notSignerAddress, - signatureHex, - ); - expect(isValidSignature).to.be.false(); - }); - it('should return true when SignatureType=Presigned and signer has presigned hash', async () => { // Presign hash const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder); diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index 81782b501..d18bf51ed 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.0.1-rc.5", + "changes": [ + { + "note": "Remove Caller and Trezor SignatureTypes", + "pr": 1015 + } + ] + }, { "version": "1.0.1-rc.4", "changes": [ diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index f8ed9cf9c..23577efdc 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -77,12 +77,6 @@ export const signatureUtils = { return signatureUtils.isValidPresignedSignatureAsync(provider, data, signerAddress); } - case SignatureType.Trezor: { - const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data, SignerType.Trezor); - const ecSignature = signatureUtils.parseECSignature(signature); - return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); - } - default: throw new Error(`Unhandled SignatureType: ${signatureTypeIndexIfExists}`); } @@ -288,10 +282,6 @@ export const signatureUtils = { signatureType = SignatureType.EthSign; break; } - case SignerType.Trezor: { - signatureType = SignatureType.Trezor; - break; - } default: throw new Error(`Unrecognized SignerType: ${signerType}`); } @@ -345,7 +335,7 @@ export const signatureUtils = { */ parseECSignature(signature: string): ECSignature { assert.isHexString('signature', signature); - const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712, SignatureType.Trezor]; + const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712]; assert.isOneOfExpectedSignatureTypes(signature, ecSignatureTypes); // tslint:disable-next-line:custom-no-magic-numbers diff --git a/packages/order-utils/test/signature_utils_test.ts b/packages/order-utils/test/signature_utils_test.ts index 4ce99a1c7..0e7831b41 100644 --- a/packages/order-utils/test/signature_utils_test.ts +++ b/packages/order-utils/test/signature_utils_test.ts @@ -76,20 +76,6 @@ describe('Signature utils', () => { ); expect(isValidSignatureLocal).to.be.true(); }); - - it('should return true for a valid Trezor signature', async () => { - dataHex = '0xd0d994e31c88f33fd8a572552a70ed339de579e5ba49ee1d17cc978bbe1cdd21'; - address = '0x6ecbe1db9ef729cbe972c83fb886247691fb6beb'; - const trezorSignature = - '0x1ce4760660e6495b5ae6723087bea073b3a99ce98ea81fdf00c240279c010e63d05b87bc34c4d67d4776e8d5aeb023a67484f4eaf0fd353b40893e5101e845cd9908'; - const isValidSignatureLocal = await signatureUtils.isValidSignatureAsync( - provider, - dataHex, - trezorSignature, - address, - ); - expect(isValidSignatureLocal).to.be.true(); - }); }); describe('#isValidECSignature', () => { const signature = { diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json index 980a12ad3..1210168d4 100644 --- a/packages/types/CHANGELOG.json +++ b/packages/types/CHANGELOG.json @@ -5,6 +5,10 @@ { "note": "Add WalletError and ValidatorError revert reasons", "pr": 1012 + }, + { + "note": "Remove Caller and Trezor SignatureTypes", + "pr": 1015 } ] }, diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index beedc24a3..5261ddfe2 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -136,7 +136,6 @@ export enum SignatureType { Wallet, Validator, PreSigned, - Trezor, NSignatureTypes, } -- cgit From 241534a63dab54172326ec71d6b5e78733bb24c6 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 24 Aug 2018 02:42:20 -0700 Subject: Fixed trezor personal message in client+contracts; added a test using message signed by Trezor One (firmware v1.6.2) --- .../contracts/test/exchange/signature_validator.ts | 18 ++++++++++++++++++ packages/order-utils/src/signature_utils.ts | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts index 9113e5248..a318d1f79 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -447,6 +447,24 @@ describe('MixinSignatureValidator', () => { ); expect(isValidSignature).to.be.false(); }); + + it('should return true when message was signed by a Trezor One (firmware version 1.6.2)', async () => { + // messageHash translates to 0x2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b + const messageHash = ethUtil.bufferToHex(ethUtil.toBuffer('++++++++++++++++++++++++++++++++')); + const signer = '0xc28b145f10f0bcf0fc000e778615f8fd73490bad'; + const v = ethUtil.toBuffer('0x1c'); + const r = ethUtil.toBuffer('0x7b888b596ccf87f0bacab0dcb483124973f7420f169b4824d7a12534ac1e9832'); + const s = ethUtil.toBuffer('0x0c8e14f7edc01459e13965f1da56e0c23ed11e2cca932571eee1292178f90424'); + const trezorSignatureType = ethUtil.toBuffer(`0x${SignatureType.EthSign}`); + const signature = Buffer.concat([v, r, s, trezorSignatureType]); + const signatureHex = ethUtil.bufferToHex(signature); + const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( + messageHash, + signer, + signatureHex, + ); + expect(isValidSignature).to.be.true(); + }); }); describe('setSignatureValidatorApproval', () => { diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index 23577efdc..c18e895f5 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -347,7 +347,7 @@ export const signatureUtils = { }; function hashTrezorPersonalMessage(message: Buffer): Buffer { - const prefix = ethUtil.toBuffer('\x19Ethereum Signed Message:\n' + String.fromCharCode(message.byteLength)); + const prefix = ethUtil.toBuffer('\x19Ethereum Signed Message:\n' + message.byteLength); return ethUtil.sha3(Buffer.concat([prefix, message])); } -- cgit From bb4d449e92da347c305d64fbe1855b13a8db361b Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 24 Aug 2018 10:59:37 -0700 Subject: Test case for Trezor Model T signature --- .../contracts/test/exchange/signature_validator.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'packages') diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts index a318d1f79..6bc0bbc02 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -465,6 +465,24 @@ describe('MixinSignatureValidator', () => { ); expect(isValidSignature).to.be.true(); }); + + it('should return true when message was signed by a Trezor Model T (firmware version 2.0.7)', async () => { + // messageHash translates to 0x2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b + const messageHash = ethUtil.bufferToHex(ethUtil.toBuffer('++++++++++++++++++++++++++++++++')); + const signer = '0x98ce6d9345e8ffa7d99ee0822272fae9d2c0e895'; + const v = ethUtil.toBuffer('0x1c'); + const r = ethUtil.toBuffer('0x423b71062c327f0ec4fe199b8da0f34185e59b4c1cb4cc23df86cac4a601fb3f'); + const s = ethUtil.toBuffer('0x53810d6591b5348b7ee08ee812c874b0fdfb942c9849d59512c90e295221091f'); + const trezorSignatureType = ethUtil.toBuffer(`0x${SignatureType.Trezor}`); + const signature = Buffer.concat([v, r, s, trezorSignatureType]); + const signatureHex = ethUtil.bufferToHex(signature); + const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( + messageHash, + signer, + signatureHex, + ); + expect(isValidSignature).to.be.true(); + }); }); describe('setSignatureValidatorApproval', () => { -- cgit From d039a1adda6fd8ce87a254310e0f56568b10136b Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 24 Aug 2018 11:57:12 -0700 Subject: Fixed linter in signatureUtils --- packages/order-utils/src/signature_utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index c18e895f5..e5478d4d6 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -347,7 +347,7 @@ export const signatureUtils = { }; function hashTrezorPersonalMessage(message: Buffer): Buffer { - const prefix = ethUtil.toBuffer('\x19Ethereum Signed Message:\n' + message.byteLength); + const prefix = ethUtil.toBuffer(`\x19Ethereum Signed Message:\n${message.byteLength}`); return ethUtil.sha3(Buffer.concat([prefix, message])); } -- cgit From a27112cbefbe9d9039d265cfc92c1022b0f9d103 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 24 Aug 2018 13:20:17 -0700 Subject: SignatureType.Trezor -> SignatureType.EthSign in Signature Validator tests --- packages/contracts/test/exchange/signature_validator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts index 6bc0bbc02..da2febfd8 100644 --- a/packages/contracts/test/exchange/signature_validator.ts +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -473,7 +473,7 @@ describe('MixinSignatureValidator', () => { const v = ethUtil.toBuffer('0x1c'); const r = ethUtil.toBuffer('0x423b71062c327f0ec4fe199b8da0f34185e59b4c1cb4cc23df86cac4a601fb3f'); const s = ethUtil.toBuffer('0x53810d6591b5348b7ee08ee812c874b0fdfb942c9849d59512c90e295221091f'); - const trezorSignatureType = ethUtil.toBuffer(`0x${SignatureType.Trezor}`); + const trezorSignatureType = ethUtil.toBuffer(`0x${SignatureType.EthSign}`); const signature = Buffer.concat([v, r, s, trezorSignatureType]); const signatureHex = ethUtil.bufferToHex(signature); const isValidSignature = await signatureValidator.publicIsValidSignature.callAsync( -- cgit From 0629a7d1434e9a17ba98be4de66fd4a7fa7ff16f Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 24 Aug 2018 14:13:54 -0700 Subject: Remove remaining Trezor references --- packages/order-utils/src/signature_utils.ts | 13 +------------ packages/order-utils/test/signature_utils_test.ts | 9 --------- packages/types/src/index.ts | 3 +-- 3 files changed, 2 insertions(+), 23 deletions(-) (limited to 'packages') diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index e5478d4d6..c0c9e71a7 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -291,7 +291,7 @@ export const signatureUtils = { /** * Combines the signature proof and the Signature Type. * @param signature The hex encoded signature proof - * @param signatureType The signature type, i.e EthSign, Trezor, Wallet etc. + * @param signatureType The signature type, i.e EthSign, Wallet etc. * @return Hex encoded string of signature proof with Signature Type */ convertToSignatureWithType(signature: string, signatureType: SignatureType): string { @@ -318,12 +318,6 @@ export const signatureUtils = { const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); return prefixedMsgHex; } - case SignerType.Trezor: { - const msgBuff = ethUtil.toBuffer(message); - const prefixedMsgBuff = hashTrezorPersonalMessage(msgBuff); - const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); - return prefixedMsgHex; - } default: throw new Error(`Unrecognized SignerType: ${signerType}`); } @@ -346,11 +340,6 @@ export const signatureUtils = { }, }; -function hashTrezorPersonalMessage(message: Buffer): Buffer { - const prefix = ethUtil.toBuffer(`\x19Ethereum Signed Message:\n${message.byteLength}`); - return ethUtil.sha3(Buffer.concat([prefix, message])); -} - function parseValidatorSignature(signature: string): ValidatorSignature { assert.isOneOfExpectedSignatureTypes(signature, [SignatureType.Validator]); // tslint:disable:custom-no-magic-numbers diff --git a/packages/order-utils/test/signature_utils_test.ts b/packages/order-utils/test/signature_utils_test.ts index 0e7831b41..2ca1109a1 100644 --- a/packages/order-utils/test/signature_utils_test.ts +++ b/packages/order-utils/test/signature_utils_test.ts @@ -256,15 +256,6 @@ describe('Signature utils', () => { r: '0xaca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d64393', s: '0x46b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf2', }; - it('should concatenate v,r,s and append the Trezor signature type', async () => { - const expectedSignatureWithSignatureType = - '0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf208'; - const signatureWithSignatureType = signatureUtils.convertECSignatureToSignatureHex( - ecSignature, - SignerType.Trezor, - ); - expect(signatureWithSignatureType).to.equal(expectedSignatureWithSignatureType); - }); it('should concatenate v,r,s and append the EthSign signature type when SignerType is Default', async () => { const expectedSignatureWithSignatureType = '0x1baca7da997ad177f040240cdccf6905b71ab16b74434388c3a72f34fd25d6439346b2bac274ff29b48b3ea6e2d04c1336eaceafda3c53ab483fc3ff12fac3ebf203'; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 5261ddfe2..4375fc631 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -140,14 +140,13 @@ export enum SignatureType { } /** - * The type of the Signer implementation. Some signer implementations use different message prefixes (e.g Trezor) or implement different + * The type of the Signer implementation. Some signer implementations use different message prefixes or implement different * eth_sign behaviour (e.g Metamask). Default assumes a spec compliant `eth_sign`. */ export enum SignerType { Default = 'DEFAULT', Ledger = 'LEDGER', Metamask = 'METAMASK', - Trezor = 'TREZOR', } export enum AssetProxyId { -- cgit From 3557cd93fc99495257cfeacc2bb58a810d25cf65 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Fri, 24 Aug 2018 14:42:07 -0700 Subject: Add testnet network info to OrderValidator artifact --- .../migrations/artifacts/2.0.0-beta-testnet/OrderValidator.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/migrations/artifacts/2.0.0-beta-testnet/OrderValidator.json b/packages/migrations/artifacts/2.0.0-beta-testnet/OrderValidator.json index 652a6d3a2..99805c766 100644 --- a/packages/migrations/artifacts/2.0.0-beta-testnet/OrderValidator.json +++ b/packages/migrations/artifacts/2.0.0-beta-testnet/OrderValidator.json @@ -782,5 +782,11 @@ } } }, - "networks": {} + "networks": { + "50": { + "address": "0xe86bb98fcf9bff3512c74589b78fb168200cc546", + "links": {}, + "constructorArgs": "[\"0x48bacb9266a570d521063ef5dd96e61686dbe788\",\"0xf47261b0000000000000000000000000871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c\"]" + } + } } \ No newline at end of file -- cgit From 80291caf7cef16f0b300484755960d92d6750a6a Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Fri, 24 Aug 2018 16:16:44 -0700 Subject: Append -Floor to getPartialAmount and isRoundingError --- .../extensions/Forwarder/MixinExchangeWrapper.sol | 4 ++-- .../extensions/Forwarder/MixinForwarderCore.sol | 4 ++-- .../src/2.0.0/extensions/Forwarder/MixinWeth.sol | 2 +- .../2.0.0/protocol/Exchange/MixinExchangeCore.sol | 8 ++++---- .../src/2.0.0/protocol/Exchange/MixinMatchOrders.sol | 4 ++-- .../protocol/Exchange/MixinWrapperFunctions.sol | 4 ++-- .../src/2.0.0/protocol/Exchange/libs/LibMath.sol | 8 ++++---- .../TestExchangeInternals/TestExchangeInternals.sol | 8 ++++---- .../contracts/src/2.0.0/test/TestLibs/TestLibs.sol | 8 ++++---- packages/contracts/test/exchange/internal.ts | 20 ++++++++++---------- packages/contracts/test/exchange/libs.ts | 6 +++--- .../test/utils/fill_order_combinatorial_utils.ts | 12 ++++++------ packages/contracts/test/utils/order_utils.ts | 2 +- packages/order-utils/src/order_state_utils.ts | 4 ++-- packages/order-utils/src/order_validation_utils.ts | 10 +++++----- packages/order-utils/src/utils.ts | 2 +- .../order-utils/test/order_validation_utils_test.ts | 12 ++++++------ 17 files changed, 59 insertions(+), 59 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol index 218713d3c..a7ff400b9 100644 --- a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol @@ -163,7 +163,7 @@ contract MixinExchangeWrapper is // Convert the remaining amount of makerAsset to buy into remaining amount // of takerAsset to sell, assuming entire amount can be sold in the current order - uint256 remainingTakerAssetFillAmount = getPartialAmount( + uint256 remainingTakerAssetFillAmount = getPartialAmountFloor( orders[i].takerAssetAmount, orders[i].makerAssetAmount, remainingMakerAssetFillAmount @@ -231,7 +231,7 @@ contract MixinExchangeWrapper is // Convert the remaining amount of ZRX to buy into remaining amount // of WETH to sell, assuming entire amount can be sold in the current order. - uint256 remainingWethSellAmount = getPartialAmount( + uint256 remainingWethSellAmount = getPartialAmountFloor( orders[i].takerAssetAmount, safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees remainingZrxBuyAmount diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinForwarderCore.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinForwarderCore.sol index 42cec4d36..14f191879 100644 --- a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinForwarderCore.sol +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinForwarderCore.sol @@ -87,7 +87,7 @@ contract MixinForwarderCore is uint256 makerAssetAmountPurchased; if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) { // Calculate amount of WETH that won't be spent on ETH fees. - wethSellAmount = getPartialAmount( + wethSellAmount = getPartialAmountFloor( PERCENTAGE_DENOMINATOR, safeAdd(PERCENTAGE_DENOMINATOR, feePercentage), msg.value @@ -103,7 +103,7 @@ contract MixinForwarderCore is makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid); } else { // 5% of WETH is reserved for filling feeOrders and paying feeRecipient. - wethSellAmount = getPartialAmount( + wethSellAmount = getPartialAmountFloor( MAX_WETH_FILL_PERCENTAGE, PERCENTAGE_DENOMINATOR, msg.value diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinWeth.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinWeth.sol index 93e85e599..5863b522d 100644 --- a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinWeth.sol +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinWeth.sol @@ -82,7 +82,7 @@ contract MixinWeth is uint256 wethRemaining = safeSub(msg.value, wethSold); // Calculate ETH fee to pay to feeRecipient. - uint256 ethFee = getPartialAmount( + uint256 ethFee = getPartialAmountFloor( feePercentage, PERCENTAGE_DENOMINATOR, wethSoldExcludingFeeOrders diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index ab5c6e507..f3a0591b2 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -320,7 +320,7 @@ contract MixinExchangeCore is // Validate fill order rounding require( - !isRoundingError( + !isRoundingErrorFloor( takerAssetFilledAmount, order.takerAssetAmount, order.makerAssetAmount @@ -376,17 +376,17 @@ contract MixinExchangeCore is { // Compute proportional transfer amounts fillResults.takerAssetFilledAmount = takerAssetFilledAmount; - fillResults.makerAssetFilledAmount = getPartialAmount( + fillResults.makerAssetFilledAmount = getPartialAmountFloor( takerAssetFilledAmount, order.takerAssetAmount, order.makerAssetAmount ); - fillResults.makerFeePaid = getPartialAmount( + fillResults.makerFeePaid = getPartialAmountFloor( takerAssetFilledAmount, order.takerAssetAmount, order.makerFee ); - fillResults.takerFeePaid = getPartialAmount( + fillResults.takerFeePaid = getPartialAmountFloor( takerAssetFilledAmount, order.takerAssetAmount, order.takerFee diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index 56b309a1b..20f4b1c33 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -183,7 +183,7 @@ contract MixinMatchOrders is leftTakerAssetFilledAmount = leftTakerAssetAmountRemaining; // The right order receives an amount proportional to how much was spent. - rightTakerAssetFilledAmount = getPartialAmount( + rightTakerAssetFilledAmount = getPartialAmountFloor( rightOrder.takerAssetAmount, rightOrder.makerAssetAmount, leftTakerAssetFilledAmount @@ -193,7 +193,7 @@ contract MixinMatchOrders is rightTakerAssetFilledAmount = rightTakerAssetAmountRemaining; // The left order receives an amount proportional to how much was spent. - leftTakerAssetFilledAmount = getPartialAmount( + leftTakerAssetFilledAmount = getPartialAmountFloor( rightOrder.makerAssetAmount, rightOrder.takerAssetAmount, rightTakerAssetFilledAmount diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol index 86194f461..1b77cfbcb 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol @@ -298,7 +298,7 @@ contract MixinWrapperFunctions is // Convert the remaining amount of makerAsset to buy into remaining amount // of takerAsset to sell, assuming entire amount can be sold in the current order - uint256 remainingTakerAssetFillAmount = getPartialAmount( + uint256 remainingTakerAssetFillAmount = getPartialAmountFloor( orders[i].takerAssetAmount, orders[i].makerAssetAmount, remainingMakerAssetFillAmount @@ -350,7 +350,7 @@ contract MixinWrapperFunctions is // Convert the remaining amount of makerAsset to buy into remaining amount // of takerAsset to sell, assuming entire amount can be sold in the current order - uint256 remainingTakerAssetFillAmount = getPartialAmount( + uint256 remainingTakerAssetFillAmount = getPartialAmountFloor( orders[i].takerAssetAmount, orders[i].makerAssetAmount, remainingMakerAssetFillAmount diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol index 90ec9e2b6..7d58f70ce 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol @@ -25,12 +25,12 @@ contract LibMath is SafeMath { - /// @dev Calculates partial value given a numerator and denominator. + /// @dev Calculates partial value given a numerator and denominator rounded down. /// @param numerator Numerator. /// @param denominator Denominator. /// @param target Value to calculate partial of. /// @return Partial value of target rounded down. - function getPartialAmount( + function getPartialAmountFloor( uint256 numerator, uint256 denominator, uint256 target @@ -48,7 +48,7 @@ contract LibMath is return partialAmount; } - /// @dev Calculates partial value given a numerator and denominator. + /// @dev Calculates partial value given a numerator and denominator rounded down. /// @param numerator Numerator. /// @param denominator Denominator. /// @param target Value to calculate partial of. @@ -79,7 +79,7 @@ contract LibMath is /// @param denominator Denominator. /// @param target Value to multiply with numerator/denominator. /// @return Rounding error is present. - function isRoundingError( + function isRoundingErrorFloor( uint256 numerator, uint256 denominator, uint256 target diff --git a/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol b/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol index 5e2ae2f0e..da9313e02 100644 --- a/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol +++ b/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol @@ -67,7 +67,7 @@ contract TestExchangeInternals is /// @param denominator Denominator. /// @param target Value to calculate partial of. /// @return Partial value of target. - function publicGetPartialAmount( + function publicGetPartialAmountFloor( uint256 numerator, uint256 denominator, uint256 target @@ -76,7 +76,7 @@ contract TestExchangeInternals is pure returns (uint256 partialAmount) { - return getPartialAmount(numerator, denominator, target); + return getPartialAmountFloor(numerator, denominator, target); } /// @dev Calculates partial value given a numerator and denominator. @@ -101,7 +101,7 @@ contract TestExchangeInternals is /// @param denominator Denominator. /// @param target Value to multiply with numerator/denominator. /// @return Rounding error is present. - function publicIsRoundingError( + function publicIsRoundingErrorFloor( uint256 numerator, uint256 denominator, uint256 target @@ -110,7 +110,7 @@ contract TestExchangeInternals is pure returns (bool isError) { - return isRoundingError(numerator, denominator, target); + return isRoundingErrorFloor(numerator, denominator, target); } /// @dev Checks if rounding error >= 0.1%. diff --git a/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol b/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol index 8c3d12226..c8c58545f 100644 --- a/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol +++ b/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol @@ -49,7 +49,7 @@ contract TestLibs is return fillOrderCalldata; } - function publicGetPartialAmount( + function publicGetPartialAmountFloor( uint256 numerator, uint256 denominator, uint256 target @@ -58,7 +58,7 @@ contract TestLibs is pure returns (uint256 partialAmount) { - partialAmount = getPartialAmount( + partialAmount = getPartialAmountFloor( numerator, denominator, target @@ -83,7 +83,7 @@ contract TestLibs is return partialAmount; } - function publicIsRoundingError( + function publicIsRoundingErrorFloor( uint256 numerator, uint256 denominator, uint256 target @@ -92,7 +92,7 @@ contract TestLibs is pure returns (bool isError) { - isError = isRoundingError( + isError = isRoundingErrorFloor( numerator, denominator, target diff --git a/packages/contracts/test/exchange/internal.ts b/packages/contracts/test/exchange/internal.ts index 0c6ab3707..14c0ff4cb 100644 --- a/packages/contracts/test/exchange/internal.ts +++ b/packages/contracts/test/exchange/internal.ts @@ -75,7 +75,7 @@ describe('Exchange core internal functions', () => { // Note(albrow): Don't forget to add beforeEach and afterEach calls to reset // the blockchain state for any tests which modify it! - async function referenceGetPartialAmountAsync( + async function referenceGetPartialAmountFloorAsync( numerator: BigNumber, denominator: BigNumber, target: BigNumber, @@ -165,18 +165,18 @@ describe('Exchange core internal functions', () => { // implementation or the Solidity implementation of // calculateFillResults. return { - makerAssetFilledAmount: await referenceGetPartialAmountAsync( + makerAssetFilledAmount: await referenceGetPartialAmountFloorAsync( takerAssetFilledAmount, orderTakerAssetAmount, otherAmount, ), takerAssetFilledAmount, - makerFeePaid: await referenceGetPartialAmountAsync( + makerFeePaid: await referenceGetPartialAmountFloorAsync( takerAssetFilledAmount, orderTakerAssetAmount, otherAmount, ), - takerFeePaid: await referenceGetPartialAmountAsync( + takerFeePaid: await referenceGetPartialAmountFloorAsync( takerAssetFilledAmount, orderTakerAssetAmount, otherAmount, @@ -199,18 +199,18 @@ describe('Exchange core internal functions', () => { ); }); - describe('getPartialAmount', async () => { - async function testGetPartialAmountAsync( + describe('getPartialAmountFloor', async () => { + async function testGetPartialAmountFloorAsync( numerator: BigNumber, denominator: BigNumber, target: BigNumber, ): Promise { - return testExchange.publicGetPartialAmount.callAsync(numerator, denominator, target); + return testExchange.publicGetPartialAmountFloor.callAsync(numerator, denominator, target); } await testCombinatoriallyWithReferenceFuncAsync( 'getPartialAmount', - referenceGetPartialAmountAsync, - testGetPartialAmountAsync, + referenceGetPartialAmountFloorAsync, + testGetPartialAmountFloorAsync, [uint256Values, uint256Values, uint256Values], ); }); @@ -284,7 +284,7 @@ describe('Exchange core internal functions', () => { denominator: BigNumber, target: BigNumber, ): Promise { - return testExchange.publicIsRoundingError.callAsync(numerator, denominator, target); + return testExchange.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target); } await testCombinatoriallyWithReferenceFuncAsync( 'isRoundingError', diff --git a/packages/contracts/test/exchange/libs.ts b/packages/contracts/test/exchange/libs.ts index a5f31a498..37234489e 100644 --- a/packages/contracts/test/exchange/libs.ts +++ b/packages/contracts/test/exchange/libs.ts @@ -77,7 +77,7 @@ describe('Exchange libs', () => { const denominator = new BigNumber(999); const target = new BigNumber(50); // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1% - const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + const isRoundingError = await libs.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target); expect(isRoundingError).to.be.true(); }); it('should return false if there is a rounding of 0.09%', async () => { @@ -85,7 +85,7 @@ describe('Exchange libs', () => { const denominator = new BigNumber(9991); const target = new BigNumber(500); // rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09% - const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + const isRoundingError = await libs.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target); expect(isRoundingError).to.be.false(); }); it('should return true if there is a rounding error of 0.11%', async () => { @@ -93,7 +93,7 @@ describe('Exchange libs', () => { const denominator = new BigNumber(9989); const target = new BigNumber(500); // rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011% - const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + const isRoundingError = await libs.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target); expect(isRoundingError).to.be.true(); }); }); diff --git a/packages/contracts/test/utils/fill_order_combinatorial_utils.ts b/packages/contracts/test/utils/fill_order_combinatorial_utils.ts index a9318571c..92d0f4003 100644 --- a/packages/contracts/test/utils/fill_order_combinatorial_utils.ts +++ b/packages/contracts/test/utils/fill_order_combinatorial_utils.ts @@ -467,17 +467,17 @@ export class FillOrderCombinatorialUtils { ? remainingTakerAmountToFill : alreadyFilledTakerAmount.add(takerAssetFillAmount); - const expFilledMakerAmount = orderUtils.getPartialAmount( + const expFilledMakerAmount = orderUtils.getPartialAmountFloor( expFilledTakerAmount, signedOrder.takerAssetAmount, signedOrder.makerAssetAmount, ); - const expMakerFeePaid = orderUtils.getPartialAmount( + const expMakerFeePaid = orderUtils.getPartialAmountFloor( expFilledTakerAmount, signedOrder.takerAssetAmount, signedOrder.makerFee, ); - const expTakerFeePaid = orderUtils.getPartialAmount( + const expTakerFeePaid = orderUtils.getPartialAmountFloor( expFilledTakerAmount, signedOrder.takerAssetAmount, signedOrder.takerFee, @@ -668,7 +668,7 @@ export class FillOrderCombinatorialUtils { signedOrder: SignedOrder, takerAssetFillAmount: BigNumber, ): Promise { - const makerAssetFillAmount = orderUtils.getPartialAmount( + const makerAssetFillAmount = orderUtils.getPartialAmountFloor( takerAssetFillAmount, signedOrder.takerAssetAmount, signedOrder.makerAssetAmount, @@ -705,7 +705,7 @@ export class FillOrderCombinatorialUtils { ); } - const makerFee = orderUtils.getPartialAmount( + const makerFee = orderUtils.getPartialAmountFloor( takerAssetFillAmount, signedOrder.takerAssetAmount, signedOrder.makerFee, @@ -829,7 +829,7 @@ export class FillOrderCombinatorialUtils { ); } - const takerFee = orderUtils.getPartialAmount( + const takerFee = orderUtils.getPartialAmountFloor( takerAssetFillAmount, signedOrder.takerAssetAmount, signedOrder.takerFee, diff --git a/packages/contracts/test/utils/order_utils.ts b/packages/contracts/test/utils/order_utils.ts index 019f6e74b..444e27c44 100644 --- a/packages/contracts/test/utils/order_utils.ts +++ b/packages/contracts/test/utils/order_utils.ts @@ -5,7 +5,7 @@ import { constants } from './constants'; import { CancelOrder, MatchOrder } from './types'; export const orderUtils = { - getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber { + getPartialAmountFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber { const partialAmount = numerator .mul(target) .div(denominator) diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts index a0e24acf0..8398776aa 100644 --- a/packages/order-utils/src/order_state_utils.ts +++ b/packages/order-utils/src/order_state_utils.ts @@ -81,7 +81,7 @@ export class OrderStateUtils { const remainingTakerAssetAmount = signedOrder.takerAssetAmount.minus( sidedOrderRelevantState.filledTakerAssetAmount, ); - const isRoundingError = OrderValidationUtils.isRoundingError( + const isRoundingError = OrderValidationUtils.isRoundingErrorFloor( remainingTakerAssetAmount, signedOrder.takerAssetAmount, signedOrder.makerAssetAmount, @@ -191,7 +191,7 @@ export class OrderStateUtils { ); const remainingFillableTakerAssetAmountGivenMakersStatus = signedOrder.makerAssetAmount.eq(0) ? new BigNumber(0) - : utils.getPartialAmount( + : utils.getPartialAmountFloor( orderRelevantMakerState.remainingFillableAssetAmount, signedOrder.makerAssetAmount, signedOrder.takerAssetAmount, diff --git a/packages/order-utils/src/order_validation_utils.ts b/packages/order-utils/src/order_validation_utils.ts index 972e6f6d6..8227fb07c 100644 --- a/packages/order-utils/src/order_validation_utils.ts +++ b/packages/order-utils/src/order_validation_utils.ts @@ -24,7 +24,7 @@ export class OrderValidationUtils { * @param denominator Denominator value. When used to check an order, pass in `order.takerAssetAmount` * @param target Target value. When used to check an order, pass in `order.makerAssetAmount` */ - public static isRoundingError(numerator: BigNumber, denominator: BigNumber, target: BigNumber): boolean { + public static isRoundingErrorFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): boolean { // Solidity's mulmod() in JS // Source: https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#mathematical-and-cryptographic-functions if (denominator.eq(0)) { @@ -58,7 +58,7 @@ export class OrderValidationUtils { zrxAssetData: string, ): Promise { try { - const fillMakerTokenAmount = utils.getPartialAmount( + const fillMakerTokenAmount = utils.getPartialAmountFloor( fillTakerAssetAmount, signedOrder.takerAssetAmount, signedOrder.makerAssetAmount, @@ -79,7 +79,7 @@ export class OrderValidationUtils { TradeSide.Taker, TransferType.Trade, ); - const makerFeeAmount = utils.getPartialAmount( + const makerFeeAmount = utils.getPartialAmountFloor( fillTakerAssetAmount, signedOrder.takerAssetAmount, signedOrder.makerFee, @@ -92,7 +92,7 @@ export class OrderValidationUtils { TradeSide.Maker, TransferType.Fee, ); - const takerFeeAmount = utils.getPartialAmount( + const takerFeeAmount = utils.getPartialAmountFloor( fillTakerAssetAmount, signedOrder.takerAssetAmount, signedOrder.takerFee, @@ -218,7 +218,7 @@ export class OrderValidationUtils { zrxAssetData, ); - const wouldRoundingErrorOccur = OrderValidationUtils.isRoundingError( + const wouldRoundingErrorOccur = OrderValidationUtils.isRoundingErrorFloor( desiredFillTakerTokenAmount, signedOrder.takerAssetAmount, signedOrder.makerAssetAmount, diff --git a/packages/order-utils/src/utils.ts b/packages/order-utils/src/utils.ts index 7aaaf0609..0ff05e8ed 100644 --- a/packages/order-utils/src/utils.ts +++ b/packages/order-utils/src/utils.ts @@ -12,7 +12,7 @@ export const utils = { const milisecondsInSecond = 1000; return new BigNumber(Date.now() / milisecondsInSecond).round(); }, - getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber { + getPartialAmountFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber { const fillMakerTokenAmount = numerator .mul(target) .div(denominator) diff --git a/packages/order-utils/test/order_validation_utils_test.ts b/packages/order-utils/test/order_validation_utils_test.ts index d3ff867d7..d3133c0a6 100644 --- a/packages/order-utils/test/order_validation_utils_test.ts +++ b/packages/order-utils/test/order_validation_utils_test.ts @@ -16,7 +16,7 @@ describe('OrderValidationUtils', () => { const denominator = new BigNumber(999); const target = new BigNumber(50); // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1% - const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); + const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target); expect(isRoundingError).to.be.false(); }); @@ -25,7 +25,7 @@ describe('OrderValidationUtils', () => { const denominator = new BigNumber(9991); const target = new BigNumber(500); // rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09% - const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); + const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target); expect(isRoundingError).to.be.false(); }); @@ -34,7 +34,7 @@ describe('OrderValidationUtils', () => { const denominator = new BigNumber(9989); const target = new BigNumber(500); // rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011% - const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); + const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target); expect(isRoundingError).to.be.true(); }); @@ -43,7 +43,7 @@ describe('OrderValidationUtils', () => { const denominator = new BigNumber(7); const target = new BigNumber(10); // rounding error = ((3*10/7) - floor(3*10/7)) / (3*10/7) = 6.67% - const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); + const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target); expect(isRoundingError).to.be.true(); }); @@ -52,7 +52,7 @@ describe('OrderValidationUtils', () => { const denominator = new BigNumber(2); const target = new BigNumber(10); - const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); + const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target); expect(isRoundingError).to.be.false(); }); @@ -63,7 +63,7 @@ describe('OrderValidationUtils', () => { const target = new BigNumber(105762562); // rounding error = ((76564*105762562/676373677) - floor(76564*105762562/676373677)) / // (76564*105762562/676373677) = 0.0007% - const isRoundingError = OrderValidationUtils.isRoundingError(numerator, denominator, target); + const isRoundingError = OrderValidationUtils.isRoundingErrorFloor(numerator, denominator, target); expect(isRoundingError).to.be.false(); }); }); -- cgit From bc686fcbf3a0f9eea0c96107b9880314027f2d02 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Fri, 24 Aug 2018 16:17:02 -0700 Subject: Stylistic fixes --- .../src/2.0.0/protocol/Exchange/libs/LibMath.sol | 25 +++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol index 7d58f70ce..0e0fba5d2 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol @@ -39,7 +39,10 @@ contract LibMath is pure returns (uint256 partialAmount) { - require(denominator > 0, "DIVISION_BY_ZERO"); + require( + denominator > 0, + "DIVISION_BY_ZERO" + ); partialAmount = safeDiv( safeMul(numerator, target), @@ -62,13 +65,19 @@ contract LibMath is pure returns (uint256 partialAmount) { - require(denominator > 0, "DIVISION_BY_ZERO"); + require( + denominator > 0, + "DIVISION_BY_ZERO" + ); // safeDiv computes `floor(a / b)`. We use the identity (a, b integer): // ceil(a / b) = floor((a + b - 1) / b) // To implement `ceil(a / b)` using safeDiv. partialAmount = safeDiv( - safeAdd(safeMul(numerator, target), safeSub(denominator, 1)), + safeAdd( + safeMul(numerator, target), + safeSub(denominator, 1) + ), denominator ); return partialAmount; @@ -88,7 +97,10 @@ contract LibMath is pure returns (bool isError) { - require(denominator > 0, "DIVISION_BY_ZERO"); + require( + denominator > 0, + "DIVISION_BY_ZERO" + ); // The absolute rounding error is the difference between the rounded // value and the ideal value. The relative rounding error is the @@ -135,7 +147,10 @@ contract LibMath is pure returns (bool isError) { - require(denominator > 0, "DIVISION_BY_ZERO"); + require( + denominator > 0, + "DIVISION_BY_ZERO" + ); // See the comments in `isRoundingError`. if (target == 0 || numerator == 0) { -- cgit From 11328bd93d498abfcfbfa38ed69db8cb268f12be Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Fri, 24 Aug 2018 16:26:48 -0700 Subject: Skip self-transfers --- .../contracts/src/2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol index e9f882194..80475e6e3 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol @@ -83,7 +83,7 @@ contract MixinAssetProxyDispatcher is internal { // Do nothing if no amount should be transferred. - if (amount > 0) { + if (amount > 0 && from != to) { // Ensure assetData length is valid require( assetData.length > 3, -- cgit From e706fa76acfbf933479f767749755446cdaf438a Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Fri, 17 Aug 2018 12:11:51 -0700 Subject: Add overfill and price assertion to assertValidFill --- .../2.0.0/protocol/Exchange/MixinExchangeCore.sol | 32 ++++++++++++++++++++-- .../protocol/Exchange/mixins/MExchangeCore.sol | 2 ++ 2 files changed, 32 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index ab5c6e507..e6b2ddf3d 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -266,6 +266,7 @@ contract MixinExchangeCore is /// @param takerAddress Address of order taker. /// @param takerAssetFillAmount Desired amount of order to fill by taker. /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. + /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered. /// @param signature Proof that the orders was created by its maker. function assertValidFill( Order memory order, @@ -273,6 +274,7 @@ contract MixinExchangeCore is address takerAddress, uint256 takerAssetFillAmount, uint256 takerAssetFilledAmount, + uint256 makerAssetFilledAmount, bytes memory signature ) internal @@ -297,7 +299,7 @@ contract MixinExchangeCore is "INVALID_SENDER" ); } - + // Validate taker is allowed to fill this order if (order.takerAddress != address(0)) { require( @@ -317,7 +319,33 @@ contract MixinExchangeCore is "INVALID_ORDER_SIGNATURE" ); } - + + // Make sure taker does not pay more than desired amount + // NOTE: This assertion should never fail, it is here + // as an extra defence against potential bugs. + require( + takerAssetFilledAmount <= takerAssetFilledAmount, + "BUG_TAKER_OVERPAY" + ); + + // Make sure order is not overfilled + // NOTE: This assertion should never fail, it is here + // as an extra defence against potential bugs. + require( + safeAdd(orderInfo.orderTakerAssetFilledAmount, takerAssetFilledAmount) <= order.takerAssetAmount, + "BUG_ORDER_OVERFILL" + ); + + // Make sure order is filled at acceptable price + // NOTE: This assertion should never fail, it is here + // as an extra defence against potential bugs. + require( + safeMul(makerAssetFilledAmount, order.takerAssetAmount) + <= + safeMul(takerAssetFilledAmount, order.makerAssetAmount), + "BUG_ORDER_FILL_PRICING" + ); + // Validate fill order rounding require( !isRoundingError( diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol index c165b647c..eccb6a29d 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol @@ -90,6 +90,7 @@ contract MExchangeCore is /// @param takerAddress Address of order taker. /// @param takerAssetFillAmount Desired amount of order to fill by taker. /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. + /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered. /// @param signature Proof that the orders was created by its maker. function assertValidFill( LibOrder.Order memory order, @@ -97,6 +98,7 @@ contract MExchangeCore is address takerAddress, uint256 takerAssetFillAmount, uint256 takerAssetFilledAmount, + uint256 makerAssetFilledAmount, bytes memory signature ) internal -- cgit From d92fd437911a5b9c0af15322e39dd4c2a1f4ab60 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Fri, 17 Aug 2018 12:12:27 -0700 Subject: Update for new assertValidFill signature --- .../contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol | 9 +++++---- .../contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol | 4 +++- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index e6b2ddf3d..36060a1b6 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -98,6 +98,9 @@ contract MixinExchangeCore is uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount); uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount); + // Compute proportional fill amounts + fillResults = calculateFillResults(order, takerAssetFilledAmount); + // Validate context assertValidFill( order, @@ -105,12 +108,10 @@ contract MixinExchangeCore is takerAddress, takerAssetFillAmount, takerAssetFilledAmount, + fillResults.makerAssetFilledAmount, signature ); - - // Compute proportional fill amounts - fillResults = calculateFillResults(order, takerAssetFilledAmount); - + // Update exchange internal state updateFilledState( order, diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index 56b309a1b..4d0411a63 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -80,6 +80,7 @@ contract MixinMatchOrders is takerAddress, matchedFillResults.left.takerAssetFilledAmount, matchedFillResults.left.takerAssetFilledAmount, + matchedFillResults.left.makerAssetFilledAmount, leftSignature ); assertValidFill( @@ -88,9 +89,10 @@ contract MixinMatchOrders is takerAddress, matchedFillResults.right.takerAssetFilledAmount, matchedFillResults.right.takerAssetFilledAmount, + matchedFillResults.right.makerAssetFilledAmount, rightSignature ); - + // Update exchange state updateFilledState( leftOrder, -- cgit From b16f5f55fb66aac624dfb82b881026b588f66b79 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Fri, 17 Aug 2018 14:19:19 -0700 Subject: Check fillable early --- .../contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index 36060a1b6..e281de264 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -90,6 +90,12 @@ contract MixinExchangeCore is { // Fetch order info OrderInfo memory orderInfo = getOrderInfo(order); + + // An order can only be filled if its status is FILLABLE. + require( + orderInfo.orderStatus == uint8(OrderStatus.FILLABLE), + "ORDER_UNFILLABLE" + ); // Fetch taker address address takerAddress = getCurrentContextAddress(); -- cgit From 07e56b3cc7f1171cb199b5f0e286971d0704adb6 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Thu, 23 Aug 2018 15:33:46 -0700 Subject: Fix taker overpay check --- packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index e281de264..b3671e7e8 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -331,7 +331,7 @@ contract MixinExchangeCore is // NOTE: This assertion should never fail, it is here // as an extra defence against potential bugs. require( - takerAssetFilledAmount <= takerAssetFilledAmount, + takerAssetFilledAmount <= takerAssetFillAmount, "BUG_TAKER_OVERPAY" ); -- cgit From a1d89435525795f6f412a823241740f10cbdf1d8 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Thu, 23 Aug 2018 16:00:01 -0700 Subject: Document accetable price check --- .../src/2.0.0/protocol/Exchange/MixinExchangeCore.sol | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index b3671e7e8..e5bd306dd 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -343,13 +343,27 @@ contract MixinExchangeCore is "BUG_ORDER_OVERFILL" ); - // Make sure order is filled at acceptable price + // Make sure order is filled at acceptable price. + // The order has an implied price from the makers perspective: + // order price = order.makerAssetAmount / order.takerAssetAmount + // i.e. the number of makerAsset maker is paying per takerAsset. The + // maker is guaranteed to get this price or a better (lower) one. The + // actual price maker is getting in this fill is: + // fill price = makerAssetFilledAmount / takerAssetFilledAmount + // We need `fill price <= order price` for the fill to be fair to maker. + // This amounts to: + // makerAssetFilledAmount order.makerAssetAmount + // ------------------------ <= ----------------------- + // takerAssetFilledAmount order.takerAssetAmount + // or, equivalently: + // makerAssetFilledAmount * order.takerAssetAmount <= + // order.makerAssetAmount * takerAssetFilledAmount // NOTE: This assertion should never fail, it is here // as an extra defence against potential bugs. require( safeMul(makerAssetFilledAmount, order.takerAssetAmount) <= - safeMul(takerAssetFilledAmount, order.makerAssetAmount), + safeMul(order.makerAssetAmount, takerAssetFilledAmount), "BUG_ORDER_FILL_PRICING" ); -- cgit From e6e7bae4459595343e07c26891577a004b0062eb Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Thu, 23 Aug 2018 16:51:31 -0700 Subject: Remove BUG_ from revert reasons --- .../contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index e5bd306dd..1a0a1a135 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -332,7 +332,7 @@ contract MixinExchangeCore is // as an extra defence against potential bugs. require( takerAssetFilledAmount <= takerAssetFillAmount, - "BUG_TAKER_OVERPAY" + "TAKER_OVERPAY" ); // Make sure order is not overfilled @@ -340,7 +340,7 @@ contract MixinExchangeCore is // as an extra defence against potential bugs. require( safeAdd(orderInfo.orderTakerAssetFilledAmount, takerAssetFilledAmount) <= order.takerAssetAmount, - "BUG_ORDER_OVERFILL" + "ORDER_OVERFILL" ); // Make sure order is filled at acceptable price. @@ -364,7 +364,7 @@ contract MixinExchangeCore is safeMul(makerAssetFilledAmount, order.takerAssetAmount) <= safeMul(order.makerAssetAmount, takerAssetFilledAmount), - "BUG_ORDER_FILL_PRICING" + "INVALID_FILL_PRICE" ); // Validate fill order rounding -- cgit From 749c6ecc30d1e52de4ef4f0da5864024d75d2ecb Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Thu, 23 Aug 2018 16:55:13 -0700 Subject: Add revert reasons to types --- packages/types/src/index.ts | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'packages') diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 4375fc631..486f71a3a 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -165,6 +165,7 @@ export interface ERC721AssetData { tokenId: BigNumber; } +// TODO: DRY. These should be extracted from contract code. export enum RevertReason { OrderUnfillable = 'ORDER_UNFILLABLE', InvalidMaker = 'INVALID_MAKER', @@ -176,6 +177,10 @@ export enum RevertReason { InvalidSignature = 'INVALID_SIGNATURE', SignatureIllegal = 'SIGNATURE_ILLEGAL', SignatureUnsupported = 'SIGNATURE_UNSUPPORTED', + InvalidOrderSignature = 'INVALID_ORDER_SIGNATURE', + TakerOverpay = 'TAKER_OVERPAY', + OrderOverfill = 'ORDER_OVERFILL', + InvalidFillPrice = 'INVALID_FILL_PRICE', InvalidNewOrderEpoch = 'INVALID_NEW_ORDER_EPOCH', CompleteFillFailed = 'COMPLETE_FILL_FAILED', NegativeSpreadRequired = 'NEGATIVE_SPREAD_REQUIRED', -- cgit From 3e4493b389334fa28ae0f0043e4dbda23f21adec Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Fri, 24 Aug 2018 10:49:05 -0700 Subject: Disallow self filling --- .../contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index 1a0a1a135..b8b3899e8 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -314,7 +314,13 @@ contract MixinExchangeCore is "INVALID_TAKER" ); } - + + // Orders can not be self-filled (use cancel instead) + require( + order.makerAddress != takerAddress, + "INVALID_TAKER" + ); + // Validate Maker signature (check only if first time seen) if (orderInfo.orderTakerAssetFilledAmount == 0) { require( -- cgit From 29971f36cf5c0096dfd3b52c014c52b812a9e9ac Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Fri, 24 Aug 2018 13:41:56 -0700 Subject: Split into assertFillable and assertValidFill --- .../2.0.0/protocol/Exchange/MixinExchangeCore.sol | 54 ++++++++++++---------- .../2.0.0/protocol/Exchange/MixinMatchOrders.sol | 18 ++++++-- 2 files changed, 43 insertions(+), 29 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index b8b3899e8..4fd71092c 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -91,14 +91,11 @@ contract MixinExchangeCore is // Fetch order info OrderInfo memory orderInfo = getOrderInfo(order); - // An order can only be filled if its status is FILLABLE. - require( - orderInfo.orderStatus == uint8(OrderStatus.FILLABLE), - "ORDER_UNFILLABLE" - ); - // Fetch taker address address takerAddress = getCurrentContextAddress(); + + // Assert that the order is fillable by taker + assertFillableOrder(order, orderInfo, taker, signature); // Get amount of takerAsset to fill uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount); @@ -110,8 +107,6 @@ contract MixinExchangeCore is // Validate context assertValidFill( order, - orderInfo, - takerAddress, takerAssetFillAmount, takerAssetFilledAmount, fillResults.makerAssetFilledAmount, @@ -266,22 +261,16 @@ contract MixinExchangeCore is order.takerAssetData ); } - + /// @dev Validates context for fillOrder. Succeeds or throws. /// @param order to be filled. /// @param orderInfo OrderStatus, orderHash, and amount already filled of order. /// @param takerAddress Address of order taker. - /// @param takerAssetFillAmount Desired amount of order to fill by taker. - /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. - /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered. /// @param signature Proof that the orders was created by its maker. - function assertValidFill( + function assertFillableOrder( Order memory order, OrderInfo memory orderInfo, - address takerAddress, - uint256 takerAssetFillAmount, - uint256 takerAssetFilledAmount, - uint256 makerAssetFilledAmount, + address taker, bytes memory signature ) internal @@ -292,13 +281,7 @@ contract MixinExchangeCore is orderInfo.orderStatus == uint8(OrderStatus.FILLABLE), "ORDER_UNFILLABLE" ); - - // Revert if fill amount is invalid - require( - takerAssetFillAmount != 0, - "INVALID_TAKER_AMOUNT" - ); - + // Validate sender is allowed to fill this order if (order.senderAddress != address(0)) { require( @@ -332,6 +315,29 @@ contract MixinExchangeCore is "INVALID_ORDER_SIGNATURE" ); } + } + + /// @dev Validates context for fillOrder. Succeeds or throws. + /// @param order to be filled. + /// @param takerAssetFillAmount Desired amount of order to fill by taker. + /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. + /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered. + /// @param signature Proof that the orders was created by its maker. + function assertValidFill( + Order memory order, + uint256 takerAssetFillAmount, + uint256 takerAssetFilledAmount, + uint256 makerAssetFilledAmount, + bytes memory signature + ) + internal + view + { + // Revert if fill amount is invalid + require( + takerAssetFillAmount != 0, + "INVALID_TAKER_AMOUNT" + ); // Make sure taker does not pay more than desired amount // NOTE: This assertion should never fail, it is here diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index 4d0411a63..5a5b0376e 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -61,8 +61,20 @@ contract MixinMatchOrders is // Fetch taker address address takerAddress = getCurrentContextAddress(); - + // Either our context is valid or we revert + assertFillableOrder( + leftOrder, + leftOrderInfo, + takerAddress, + leftSignature + ); + assertFillableOrder( + righttOrder, + righttOrderInfo, + takerAddress, + leftSignature + ); assertValidMatch(leftOrder, rightOrder); // Compute proportional fill amounts @@ -76,8 +88,6 @@ contract MixinMatchOrders is // Validate fill contexts assertValidFill( leftOrder, - leftOrderInfo, - takerAddress, matchedFillResults.left.takerAssetFilledAmount, matchedFillResults.left.takerAssetFilledAmount, matchedFillResults.left.makerAssetFilledAmount, @@ -85,8 +95,6 @@ contract MixinMatchOrders is ); assertValidFill( rightOrder, - rightOrderInfo, - takerAddress, matchedFillResults.right.takerAssetFilledAmount, matchedFillResults.right.takerAssetFilledAmount, matchedFillResults.right.makerAssetFilledAmount, -- cgit From e6f5cac87887709b5a3baaec059005301723f0a5 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Fri, 24 Aug 2018 13:42:10 -0700 Subject: Fix double definition of error --- packages/types/src/index.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'packages') diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 486f71a3a..5576a6678 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -177,7 +177,6 @@ export enum RevertReason { InvalidSignature = 'INVALID_SIGNATURE', SignatureIllegal = 'SIGNATURE_ILLEGAL', SignatureUnsupported = 'SIGNATURE_UNSUPPORTED', - InvalidOrderSignature = 'INVALID_ORDER_SIGNATURE', TakerOverpay = 'TAKER_OVERPAY', OrderOverfill = 'ORDER_OVERFILL', InvalidFillPrice = 'INVALID_FILL_PRICE', -- cgit From e21599285941a092a6c6f2dbf58f14f467dcca85 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Fri, 24 Aug 2018 16:13:17 -0700 Subject: Fix mixin api --- .../2.0.0/protocol/Exchange/MixinExchangeCore.sol | 21 +++++++++++++-------- .../2.0.0/protocol/Exchange/MixinMatchOrders.sol | 14 +++++++------- .../protocol/Exchange/mixins/MExchangeCore.sol | 22 ++++++++++++++++------ 3 files changed, 36 insertions(+), 21 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index 4fd71092c..dc62db448 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -95,7 +95,12 @@ contract MixinExchangeCore is address takerAddress = getCurrentContextAddress(); // Assert that the order is fillable by taker - assertFillableOrder(order, orderInfo, taker, signature); + assertFillableOrder( + order, + orderInfo, + takerAddress, + signature + ); // Get amount of takerAsset to fill uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount); @@ -107,10 +112,10 @@ contract MixinExchangeCore is // Validate context assertValidFill( order, + orderInfo, takerAssetFillAmount, takerAssetFilledAmount, - fillResults.makerAssetFilledAmount, - signature + fillResults.makerAssetFilledAmount ); // Update exchange internal state @@ -270,7 +275,7 @@ contract MixinExchangeCore is function assertFillableOrder( Order memory order, OrderInfo memory orderInfo, - address taker, + address takerAddress, bytes memory signature ) internal @@ -319,16 +324,16 @@ contract MixinExchangeCore is /// @dev Validates context for fillOrder. Succeeds or throws. /// @param order to be filled. + /// @param orderInfo OrderStatus, orderHash, and amount already filled of order. /// @param takerAssetFillAmount Desired amount of order to fill by taker. /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered. - /// @param signature Proof that the orders was created by its maker. function assertValidFill( Order memory order, - uint256 takerAssetFillAmount, + OrderInfo memory orderInfo, + uint256 takerAssetFillAmount, // TODO: use FillResults uint256 takerAssetFilledAmount, - uint256 makerAssetFilledAmount, - bytes memory signature + uint256 makerAssetFilledAmount ) internal view diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index 5a5b0376e..c860640c4 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -70,10 +70,10 @@ contract MixinMatchOrders is leftSignature ); assertFillableOrder( - righttOrder, - righttOrderInfo, + rightOrder, + rightOrderInfo, takerAddress, - leftSignature + rightSignature ); assertValidMatch(leftOrder, rightOrder); @@ -88,17 +88,17 @@ contract MixinMatchOrders is // Validate fill contexts assertValidFill( leftOrder, + leftOrderInfo, matchedFillResults.left.takerAssetFilledAmount, matchedFillResults.left.takerAssetFilledAmount, - matchedFillResults.left.makerAssetFilledAmount, - leftSignature + matchedFillResults.left.makerAssetFilledAmount ); assertValidFill( rightOrder, + rightOrderInfo, matchedFillResults.right.takerAssetFilledAmount, matchedFillResults.right.takerAssetFilledAmount, - matchedFillResults.right.makerAssetFilledAmount, - rightSignature + matchedFillResults.right.makerAssetFilledAmount ); // Update exchange state diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol index eccb6a29d..708cb329e 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol @@ -83,23 +83,33 @@ contract MExchangeCore is bytes32 orderHash ) internal; - + /// @dev Validates context for fillOrder. Succeeds or throws. /// @param order to be filled. - /// @param orderInfo Status, orderHash, and amount already filled of order. + /// @param orderInfo OrderStatus, orderHash, and amount already filled of order. /// @param takerAddress Address of order taker. + /// @param signature Proof that the orders was created by its maker. + function assertFillableOrder( + LibOrder.Order memory order, + LibOrder.OrderInfo memory orderInfo, + address takerAddress, + bytes memory signature + ) + internal + view; + + /// @dev Validates context for fillOrder. Succeeds or throws. + /// @param order to be filled. + /// @param orderInfo Status, orderHash, and amount already filled of order. /// @param takerAssetFillAmount Desired amount of order to fill by taker. /// @param takerAssetFilledAmount Amount of takerAsset that will be filled. /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered. - /// @param signature Proof that the orders was created by its maker. function assertValidFill( LibOrder.Order memory order, LibOrder.OrderInfo memory orderInfo, - address takerAddress, uint256 takerAssetFillAmount, uint256 takerAssetFilledAmount, - uint256 makerAssetFilledAmount, - bytes memory signature + uint256 makerAssetFilledAmount ) internal view; -- cgit From cc1fac9bbee2656bdb327490de42922abfc5125a Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 24 Aug 2018 17:02:42 -0700 Subject: Fix linting errors --- packages/contracts/test/exchange/internal.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/internal.ts b/packages/contracts/test/exchange/internal.ts index 14c0ff4cb..2521665c2 100644 --- a/packages/contracts/test/exchange/internal.ts +++ b/packages/contracts/test/exchange/internal.ts @@ -220,9 +220,9 @@ describe('Exchange core internal functions', () => { numerator: BigNumber, denominator: BigNumber, target: BigNumber, - ) { + ): Promise { if (denominator.eq(0)) { - throw new Error('revert DIVISION_BY_ZERO'); + throw divisionByZeroErrorForCall; } const product = numerator.mul(target); const offset = product.add(denominator.sub(1)); -- cgit From 6b866d60533c7e46446bfb69639b07affd1aeb17 Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Fri, 24 Aug 2018 17:29:52 -0700 Subject: Revert maker not equal taker check --- .../contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index dc62db448..515606cb9 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -303,12 +303,6 @@ contract MixinExchangeCore is ); } - // Orders can not be self-filled (use cancel instead) - require( - order.makerAddress != takerAddress, - "INVALID_TAKER" - ); - // Validate Maker signature (check only if first time seen) if (orderInfo.orderTakerAssetFilledAmount == 0) { require( @@ -339,6 +333,7 @@ contract MixinExchangeCore is view { // Revert if fill amount is invalid + // TODO: reconsider necessity for v2.1 require( takerAssetFillAmount != 0, "INVALID_TAKER_AMOUNT" -- cgit From 044415e23d3a079e6301d8fb838da60456794c4f Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 20 Aug 2018 17:37:23 -0700 Subject: Remove redundant sload from getCurrentContextAddress --- packages/contracts/src/2.0.0/protocol/Exchange/MixinTransactions.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinTransactions.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinTransactions.sol index 821d30279..4a59b6c0f 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinTransactions.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinTransactions.sol @@ -155,7 +155,8 @@ contract MixinTransactions is view returns (address) { - address contextAddress = currentContextAddress == address(0) ? msg.sender : currentContextAddress; + address currentContextAddress_ = currentContextAddress; + address contextAddress = currentContextAddress_ == address(0) ? msg.sender : currentContextAddress_; return contextAddress; } } -- cgit From d8cb56caa33885397f557afac5a4ccb24efb47d0 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 20 Aug 2018 17:38:25 -0700 Subject: Add ReentrancyGuard contract --- .../utils/ReentrancyGuard/ReentrancyGuard.sol | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol b/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol new file mode 100644 index 000000000..70fc4ca6b --- /dev/null +++ b/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol @@ -0,0 +1,44 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity 0.4.24; + + +contract ReentrancyGuard { + + // Locked state of mutex + bool private locked = false; + + /// @dev Functions with this modifier cannot be reentered. + modifier nonReentrant() { + // Ensure mutex is unlocked + require( + !locked, + "REENTRANCY_ILLEGAL" + ); + + // Lock mutex before function call + locked = true; + + // Perform function call + _; + + // Unlock mutex after function call + locked = false; + } + +} -- cgit From 6f88e9bdbdd8f44c45053b8c17c84411f9499084 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Tue, 21 Aug 2018 16:06:21 -0700 Subject: Add internal fill functions, add reentrancy guard to public functions that make external calls --- .../2.0.0/protocol/Exchange/MixinExchangeCore.sol | 86 ++++++++++++++-------- .../2.0.0/protocol/Exchange/MixinMatchOrders.sol | 3 + .../protocol/Exchange/MixinWrapperFunctions.sol | 46 +++++++++--- .../protocol/Exchange/mixins/MExchangeCore.sol | 13 ++++ .../protocol/Exchange/mixins/MWrapperFunctions.sol | 40 ++++++++++ .../utils/ReentrancyGuard/ReentrancyGuard.sol | 1 - 6 files changed, 148 insertions(+), 41 deletions(-) create mode 100644 packages/contracts/src/2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index f3a0591b2..d00323b15 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -19,6 +19,7 @@ pragma solidity 0.4.24; pragma experimental ABIEncoderV2; +import "../../utils/ReentrancyGuard/ReentrancyGuard.sol"; import "./libs/LibConstants.sol"; import "./libs/LibFillResults.sol"; import "./libs/LibOrder.sol"; @@ -30,6 +31,7 @@ import "./mixins/MAssetProxyDispatcher.sol"; contract MixinExchangeCore is + ReentrancyGuard, LibConstants, LibMath, LibOrder, @@ -86,43 +88,14 @@ contract MixinExchangeCore is bytes memory signature ) public + nonReentrant returns (FillResults memory fillResults) { - // Fetch order info - OrderInfo memory orderInfo = getOrderInfo(order); - - // Fetch taker address - address takerAddress = getCurrentContextAddress(); - - // Get amount of takerAsset to fill - uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount); - uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount); - - // Validate context - assertValidFill( + fillResults = fillOrderInternal( order, - orderInfo, - takerAddress, takerAssetFillAmount, - takerAssetFilledAmount, signature ); - - // Compute proportional fill amounts - fillResults = calculateFillResults(order, takerAssetFilledAmount); - - // Update exchange internal state - updateFilledState( - order, - takerAddress, - orderInfo.orderHash, - orderInfo.orderTakerAssetFilledAmount, - fillResults - ); - - // Settle order - settleOrder(order, takerAddress, fillResults); - return fillResults; } @@ -203,6 +176,57 @@ contract MixinExchangeCore is return orderInfo; } + /// @dev Fills the input order. + /// @param order Order struct containing order specifications. + /// @param takerAssetFillAmount Desired amount of takerAsset to sell. + /// @param signature Proof that order has been created by maker. + /// @return Amounts filled and fees paid by maker and taker. + function fillOrderInternal( + Order memory order, + uint256 takerAssetFillAmount, + bytes memory signature + ) + internal + returns (FillResults memory fillResults) + { + // Fetch order info + OrderInfo memory orderInfo = getOrderInfo(order); + + // Fetch taker address + address takerAddress = getCurrentContextAddress(); + + // Get amount of takerAsset to fill + uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount); + uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount); + + // Validate context + assertValidFill( + order, + orderInfo, + takerAddress, + takerAssetFillAmount, + takerAssetFilledAmount, + signature + ); + + // Compute proportional fill amounts + fillResults = calculateFillResults(order, takerAssetFilledAmount); + + // Update exchange internal state + updateFilledState( + order, + takerAddress, + orderInfo.orderHash, + orderInfo.orderTakerAssetFilledAmount, + fillResults + ); + + // Settle order + settleOrder(order, takerAddress, fillResults); + + return fillResults; + } + /// @dev Updates state with results of a fill order. /// @param order that was filled. /// @param takerAddress Address of taker who filled the order. diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index 20f4b1c33..25220a673 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -14,6 +14,7 @@ pragma solidity 0.4.24; pragma experimental ABIEncoderV2; +import "../../utils/ReentrancyGuard/ReentrancyGuard.sol"; import "./libs/LibConstants.sol"; import "./libs/LibMath.sol"; import "./libs/LibOrder.sol"; @@ -25,6 +26,7 @@ import "./mixins/MAssetProxyDispatcher.sol"; contract MixinMatchOrders is + ReentrancyGuard, LibConstants, LibMath, MAssetProxyDispatcher, @@ -48,6 +50,7 @@ contract MixinMatchOrders is bytes memory rightSignature ) public + nonReentrant returns (LibFillResults.MatchedFillResults memory matchedFillResults) { // We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData. diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol index 1b77cfbcb..ff1cb6995 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol @@ -19,14 +19,17 @@ pragma solidity 0.4.24; pragma experimental ABIEncoderV2; +import "../../utils/ReentrancyGuard/ReentrancyGuard.sol"; import "./libs/LibMath.sol"; import "./libs/LibOrder.sol"; import "./libs/LibFillResults.sol"; import "./libs/LibAbiEncoder.sol"; import "./mixins/MExchangeCore.sol"; +import "./mixins/MWrapperFunctions.sol"; contract MixinWrapperFunctions is + ReentrancyGuard, LibMath, LibFillResults, LibAbiEncoder, @@ -43,17 +46,14 @@ contract MixinWrapperFunctions is bytes memory signature ) public + nonReentrant returns (FillResults memory fillResults) { - fillResults = fillOrder( + fillResults = fillOrKillOrderInternal( order, takerAssetFillAmount, signature ); - require( - fillResults.takerAssetFilledAmount == takerAssetFillAmount, - "COMPLETE_FILL_FAILED" - ); return fillResults; } @@ -117,11 +117,12 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public + nonReentrant returns (FillResults memory totalFillResults) { uint256 ordersLength = orders.length; for (uint256 i = 0; i != ordersLength; i++) { - FillResults memory singleFillResults = fillOrder( + FillResults memory singleFillResults = fillOrderInternal( orders[i], takerAssetFillAmounts[i], signatures[i] @@ -143,11 +144,12 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public + nonReentrant returns (FillResults memory totalFillResults) { uint256 ordersLength = orders.length; for (uint256 i = 0; i != ordersLength; i++) { - FillResults memory singleFillResults = fillOrKillOrder( + FillResults memory singleFillResults = fillOrKillOrderInternal( orders[i], takerAssetFillAmounts[i], signatures[i] @@ -195,6 +197,7 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public + nonReentrant returns (FillResults memory totalFillResults) { bytes memory takerAssetData = orders[0].takerAssetData; @@ -210,7 +213,7 @@ contract MixinWrapperFunctions is uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount); // Attempt to sell the remaining amount of takerAsset - FillResults memory singleFillResults = fillOrder( + FillResults memory singleFillResults = fillOrderInternal( orders[i], remainingTakerAssetFillAmount, signatures[i] @@ -282,6 +285,7 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public + nonReentrant returns (FillResults memory totalFillResults) { bytes memory makerAssetData = orders[0].makerAssetData; @@ -305,7 +309,7 @@ contract MixinWrapperFunctions is ); // Attempt to sell the remaining amount of takerAsset - FillResults memory singleFillResults = fillOrder( + FillResults memory singleFillResults = fillOrderInternal( orders[i], remainingTakerAssetFillAmount, signatures[i] @@ -400,4 +404,28 @@ contract MixinWrapperFunctions is } return ordersInfo; } + + /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. + /// @param order Order struct containing order specifications. + /// @param takerAssetFillAmount Desired amount of takerAsset to sell. + /// @param signature Proof that order has been created by maker. + function fillOrKillOrderInternal( + LibOrder.Order memory order, + uint256 takerAssetFillAmount, + bytes memory signature + ) + internal + returns (FillResults memory fillResults) + { + fillResults = fillOrderInternal( + order, + takerAssetFillAmount, + signature + ); + require( + fillResults.takerAssetFilledAmount == takerAssetFillAmount, + "COMPLETE_FILL_FAILED" + ); + return fillResults; + } } diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol index c165b647c..41832fe4b 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol @@ -59,6 +59,19 @@ contract MExchangeCore is uint256 orderEpoch // Orders with specified makerAddress and senderAddress with a salt less than this value are considered cancelled. ); + /// @dev Fills the input order. + /// @param order Order struct containing order specifications. + /// @param takerAssetFillAmount Desired amount of takerAsset to sell. + /// @param signature Proof that order has been created by maker. + /// @return Amounts filled and fees paid by maker and taker. + function fillOrderInternal( + LibOrder.Order memory order, + uint256 takerAssetFillAmount, + bytes memory signature + ) + internal + returns (LibFillResults.FillResults memory fillResults); + /// @dev Updates state with results of a fill order. /// @param order that was filled. /// @param takerAddress Address of taker who filled the order. diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol new file mode 100644 index 000000000..e04d4a429 --- /dev/null +++ b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol @@ -0,0 +1,40 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; + +import "../libs/LibOrder.sol"; +import "../libs/LibFillResults.sol"; +import "../interfaces/IWrapperFunctions.sol"; + + +contract MWrapperFunctions { + + /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. + /// @param order LibOrder.Order struct containing order specifications. + /// @param takerAssetFillAmount Desired amount of takerAsset to sell. + /// @param signature Proof that order has been created by maker. + function fillOrKillOrderInternal( + LibOrder.Order memory order, + uint256 takerAssetFillAmount, + bytes memory signature + ) + internal + returns (LibFillResults.FillResults memory fillResults); +} diff --git a/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol b/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol index 70fc4ca6b..5d6f6970d 100644 --- a/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol +++ b/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol @@ -40,5 +40,4 @@ contract ReentrancyGuard { // Unlock mutex after function call locked = false; } - } -- cgit From cf12daea2f3a907f6a111c12d1e67c2e8b0a8797 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Tue, 21 Aug 2018 17:53:40 -0700 Subject: Add ReentrantToken --- packages/contracts/compiler.json | 1 + packages/contracts/package.json | 2 +- .../ReentrantERC20Token/ReentrantERC20Token.sol | 173 +++++++++++++++++++++ 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol (limited to 'packages') diff --git a/packages/contracts/compiler.json b/packages/contracts/compiler.json index 27bb69a36..f66114e87 100644 --- a/packages/contracts/compiler.json +++ b/packages/contracts/compiler.json @@ -40,6 +40,7 @@ "MultiSigWallet", "MultiSigWalletWithTimeLock", "OrderValidator", + "ReentrantERC20Token", "TestAssetProxyOwner", "TestAssetProxyDispatcher", "TestConstants", diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 0ead2f43f..8d75eb4ce 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -34,7 +34,7 @@ }, "config": { "abis": - "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json" + "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json" }, "repository": { "type": "git", diff --git a/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol b/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol new file mode 100644 index 000000000..f5c98177f --- /dev/null +++ b/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol @@ -0,0 +1,173 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; + +import "../../tokens/ERC20Token/ERC20Token.sol"; +import "../../protocol/Exchange/interfaces/IExchange.sol"; +import "../../protocol/Exchange/libs/LibOrder.sol"; + + +contract ReentrantERC20Token is + ERC20Token +{ + // solhint-disable-next-line var-name-mixedcase + IExchange internal EXCHANGE; + + // All of these functions are potentially vulnerable to reentrancy + enum ExchangeFunction { + FILL_ORDER, + FILL_OR_KILL_ORDER, + FILL_ORDER_NO_THROW, + BATCH_FILL_ORDERS, + BATCH_FILL_OR_KILL_ORDERS, + BATCH_FILL_ORDERS_NO_THROW, + MARKET_BUY_ORDERS, + MARKET_BUY_ORDERS_NO_THROW, + MARKET_SELL_ORDERS, + MARKET_SELL_ORDERS_NO_THROW, + MATCH_ORDERS + } + + uint8 internal currentFunctionId = 0; + + constructor (address _exchange) + public + { + EXCHANGE = IExchange(_exchange); + } + + /// @dev Set the current function that will be called when `transferFrom` is called. + /// @param _currentFunctionId Id that corresponds to function name. + function setCurrentFunction(uint8 _currentFunctionId) + external + { + currentFunctionId = _currentFunctionId; + } + + /// @dev A version of `transferFrom` that attempts to reenter the Exchange contract. + /// @param _from The address of the sender + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + function transferFrom( + address _from, + address _to, + uint256 _value + ) + external + returns (bool) + { + // This order would normally be invalid, but it will be used strictly for testing reentrnacy. + // Any reentrancy checks will happen before any other checks that invalidate the order. + LibOrder.Order memory order = LibOrder.Order({ + makerAddress: address(0), + takerAddress: address(0), + feeRecipientAddress: address(0), + senderAddress: address(0), + makerAssetAmount: 0, + takerAssetAmount: 0, + makerFee: 0, + takerFee: 0, + expirationTimeSeconds: 0, + salt: 0, + makerAssetData: "", + takerAssetData: "" + }); + + // Initialize remaining null parameters + bytes memory signature = ""; + LibOrder.Order[] memory orders = new LibOrder.Order[](1); + orders[0] = order; + uint256[] memory takerAssetFillAmounts = new uint256[](1); + takerAssetFillAmounts[0] = 0; + bytes[] memory signatures = new bytes[](1); + signatures[0] = signature; + + // Attempt to reenter Exchange by calling function with currentFunctionId + if (currentFunctionId == uint8(ExchangeFunction.FILL_ORDER)) { + EXCHANGE.fillOrder( + order, + 0, + signature + ); + } else if (currentFunctionId == uint8(ExchangeFunction.FILL_OR_KILL_ORDER)) { + EXCHANGE.fillOrKillOrder( + order, + 0, + signature + ); + } else if (currentFunctionId == uint8(ExchangeFunction.FILL_ORDER_NO_THROW)) { + EXCHANGE.fillOrderNoThrow( + order, + 0, + signature + ); + } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS)) { + EXCHANGE.batchFillOrders( + orders, + takerAssetFillAmounts, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_OR_KILL_ORDERS)) { + EXCHANGE.batchFillOrKillOrders( + orders, + takerAssetFillAmounts, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS_NO_THROW)) { + EXCHANGE.batchFillOrdersNoThrow( + orders, + takerAssetFillAmounts, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS)) { + EXCHANGE.marketBuyOrders( + orders, + 0, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS_NO_THROW)) { + EXCHANGE.marketBuyOrdersNoThrow( + orders, + 0, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS)) { + EXCHANGE.marketSellOrders( + orders, + 0, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS_NO_THROW)) { + EXCHANGE.marketSellOrdersNoThrow( + orders, + 0, + signatures + ); + } else if (currentFunctionId == uint8(ExchangeFunction.MATCH_ORDERS)) { + EXCHANGE.matchOrders( + order, + order, + signature, + signature + ); + } + return true; + } +} \ No newline at end of file -- cgit From 6d0dedc62cb1be54dbdd287fd59d7b568a199007 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 22 Aug 2018 17:57:57 -0700 Subject: Split modifiers into check only and check, lock, unlock --- .../src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol b/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol index 5d6f6970d..2b980c7ca 100644 --- a/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol +++ b/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol @@ -31,6 +31,19 @@ contract ReentrancyGuard { "REENTRANCY_ILLEGAL" ); + // Perform function call + _; + } + + /// @dev Functions with this modifer cannot be reentered. The mutex will be locked + /// before function execution and unlocked after. + modifier lockMutex() { + // Ensure mutex is unlocked + require( + !locked, + "REENTRANCY_ILLEGAL" + ); + // Lock mutex before function call locked = true; -- cgit From c75212bef0b3801ecda09a89d5e30e359dcf1b63 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 22 Aug 2018 17:58:47 -0700 Subject: Add nonReentrant modifiers on functions that use getCurrentContextAddress only, add lockMutex modifier on functions that make external calls --- .../2.0.0/protocol/Exchange/MixinExchangeCore.sol | 4 +++- .../2.0.0/protocol/Exchange/MixinMatchOrders.sol | 2 +- .../protocol/Exchange/MixinSignatureValidator.sol | 3 +++ .../protocol/Exchange/MixinWrapperFunctions.sol | 23 ++++++++++------------ 4 files changed, 17 insertions(+), 15 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index d00323b15..f02514fc6 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -56,6 +56,7 @@ contract MixinExchangeCore is /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled. function cancelOrdersUpTo(uint256 targetOrderEpoch) external + nonReentrant { address makerAddress = getCurrentContextAddress(); // If this function is called via `executeTransaction`, we only update the orderEpoch for the makerAddress/msg.sender combination. @@ -88,7 +89,7 @@ contract MixinExchangeCore is bytes memory signature ) public - nonReentrant + lockMutex returns (FillResults memory fillResults) { fillResults = fillOrderInternal( @@ -104,6 +105,7 @@ contract MixinExchangeCore is /// @param order Order to cancel. Order must be OrderStatus.FILLABLE. function cancelOrder(Order memory order) public + nonReentrant { // Fetch current order status OrderInfo memory orderInfo = getOrderInfo(order); diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index 25220a673..39c47e6f9 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -50,7 +50,7 @@ contract MixinMatchOrders is bytes memory rightSignature ) public - nonReentrant + lockMutex returns (LibFillResults.MatchedFillResults memory matchedFillResults) { // We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData. diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol index f30adcdb8..4eb6a2fa6 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinSignatureValidator.sol @@ -19,6 +19,7 @@ pragma solidity 0.4.24; import "../../utils/LibBytes/LibBytes.sol"; +import "../../utils/ReentrancyGuard/ReentrancyGuard.sol"; import "./mixins/MSignatureValidator.sol"; import "./mixins/MTransactions.sol"; import "./interfaces/IWallet.sol"; @@ -26,6 +27,7 @@ import "./interfaces/IValidator.sol"; contract MixinSignatureValidator is + ReentrancyGuard, MSignatureValidator, MTransactions { @@ -69,6 +71,7 @@ contract MixinSignatureValidator is bool approval ) external + nonReentrant { address signerAddress = getCurrentContextAddress(); allowedValidators[signerAddress][validatorAddress] = approval; diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol index ff1cb6995..b0474b110 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol @@ -46,7 +46,7 @@ contract MixinWrapperFunctions is bytes memory signature ) public - nonReentrant + lockMutex returns (FillResults memory fillResults) { fillResults = fillOrKillOrderInternal( @@ -69,6 +69,7 @@ contract MixinWrapperFunctions is bytes memory signature ) public + nonReentrant returns (FillResults memory fillResults) { // ABI encode calldata for `fillOrder` @@ -88,14 +89,7 @@ contract MixinWrapperFunctions is fillOrderCalldata, // write output over input 128 // output size is 128 bytes ) - switch success - case 0 { - mstore(fillResults, 0) - mstore(add(fillResults, 32), 0) - mstore(add(fillResults, 64), 0) - mstore(add(fillResults, 96), 0) - } - case 1 { + if success { mstore(fillResults, mload(fillOrderCalldata)) mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32))) mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64))) @@ -117,7 +111,7 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public - nonReentrant + lockMutex returns (FillResults memory totalFillResults) { uint256 ordersLength = orders.length; @@ -144,7 +138,7 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public - nonReentrant + lockMutex returns (FillResults memory totalFillResults) { uint256 ordersLength = orders.length; @@ -172,6 +166,7 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public + nonReentrant returns (FillResults memory totalFillResults) { uint256 ordersLength = orders.length; @@ -197,7 +192,7 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public - nonReentrant + lockMutex returns (FillResults memory totalFillResults) { bytes memory takerAssetData = orders[0].takerAssetData; @@ -242,6 +237,7 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public + nonReentrant returns (FillResults memory totalFillResults) { bytes memory takerAssetData = orders[0].takerAssetData; @@ -285,7 +281,7 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public - nonReentrant + lockMutex returns (FillResults memory totalFillResults) { bytes memory makerAssetData = orders[0].makerAssetData; @@ -338,6 +334,7 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public + nonReentrant returns (FillResults memory totalFillResults) { bytes memory makerAssetData = orders[0].makerAssetData; -- cgit From 56c3c29febe6264eee7f1c3f1ed8f1dc7c4685c6 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 22 Aug 2018 18:00:01 -0700 Subject: Update ReentrantERC20Token with new functions and check that revert is occuring for correct reason --- .../ReentrantERC20Token/ReentrantERC20Token.sol | 110 ++++++++++++++------- 1 file changed, 75 insertions(+), 35 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol b/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol index f5c98177f..8d575d09a 100644 --- a/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol +++ b/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol @@ -19,6 +19,7 @@ pragma solidity 0.4.24; pragma experimental ABIEncoderV2; +import "../../utils/LibBytes/LibBytes.sol"; import "../../tokens/ERC20Token/ERC20Token.sol"; import "../../protocol/Exchange/interfaces/IExchange.sol"; import "../../protocol/Exchange/libs/LibOrder.sol"; @@ -27,9 +28,17 @@ import "../../protocol/Exchange/libs/LibOrder.sol"; contract ReentrantERC20Token is ERC20Token { + + using LibBytes for bytes; + // solhint-disable-next-line var-name-mixedcase IExchange internal EXCHANGE; + bytes internal constant REENTRANCY_ILLEGAL_REVERT_REASON = abi.encodeWithSelector( + bytes4(keccak256("Error(string)")), + "REENTRANCY_ILLEGAL" + ); + // All of these functions are potentially vulnerable to reentrancy enum ExchangeFunction { FILL_ORDER, @@ -42,7 +51,10 @@ contract ReentrantERC20Token is MARKET_BUY_ORDERS_NO_THROW, MARKET_SELL_ORDERS, MARKET_SELL_ORDERS_NO_THROW, - MATCH_ORDERS + MATCH_ORDERS, + CANCEL_ORDER, + CANCEL_ORDERS_UP_TO, + SET_SIGNATURE_VALIDATOR_APPROVAL } uint8 internal currentFunctionId = 0; @@ -75,99 +87,127 @@ contract ReentrantERC20Token is { // This order would normally be invalid, but it will be used strictly for testing reentrnacy. // Any reentrancy checks will happen before any other checks that invalidate the order. - LibOrder.Order memory order = LibOrder.Order({ - makerAddress: address(0), - takerAddress: address(0), - feeRecipientAddress: address(0), - senderAddress: address(0), - makerAssetAmount: 0, - takerAssetAmount: 0, - makerFee: 0, - takerFee: 0, - expirationTimeSeconds: 0, - salt: 0, - makerAssetData: "", - takerAssetData: "" - }); + LibOrder.Order memory order; // Initialize remaining null parameters - bytes memory signature = ""; - LibOrder.Order[] memory orders = new LibOrder.Order[](1); - orders[0] = order; - uint256[] memory takerAssetFillAmounts = new uint256[](1); - takerAssetFillAmounts[0] = 0; - bytes[] memory signatures = new bytes[](1); - signatures[0] = signature; - - // Attempt to reenter Exchange by calling function with currentFunctionId + bytes memory signature; + LibOrder.Order[] memory orders; + uint256[] memory takerAssetFillAmounts; + bytes[] memory signatures; + bytes memory calldata; + + // Create calldata for function that corresponds to currentFunctionId if (currentFunctionId == uint8(ExchangeFunction.FILL_ORDER)) { - EXCHANGE.fillOrder( + calldata = abi.encodeWithSelector( + EXCHANGE.fillOrder.selector, order, 0, signature ); } else if (currentFunctionId == uint8(ExchangeFunction.FILL_OR_KILL_ORDER)) { - EXCHANGE.fillOrKillOrder( + calldata = abi.encodeWithSelector( + EXCHANGE.fillOrKillOrder.selector, order, 0, signature ); } else if (currentFunctionId == uint8(ExchangeFunction.FILL_ORDER_NO_THROW)) { - EXCHANGE.fillOrderNoThrow( + calldata = abi.encodeWithSelector( + EXCHANGE.fillOrderNoThrow.selector, order, 0, signature ); } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS)) { - EXCHANGE.batchFillOrders( + calldata = abi.encodeWithSelector( + EXCHANGE.batchFillOrders.selector, orders, takerAssetFillAmounts, signatures ); } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_OR_KILL_ORDERS)) { - EXCHANGE.batchFillOrKillOrders( + calldata = abi.encodeWithSelector( + EXCHANGE.batchFillOrKillOrders.selector, orders, takerAssetFillAmounts, signatures ); } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS_NO_THROW)) { - EXCHANGE.batchFillOrdersNoThrow( + calldata = abi.encodeWithSelector( + EXCHANGE.batchFillOrdersNoThrow.selector, orders, takerAssetFillAmounts, signatures ); } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS)) { - EXCHANGE.marketBuyOrders( + calldata = abi.encodeWithSelector( + EXCHANGE.marketBuyOrders.selector, orders, 0, signatures ); } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS_NO_THROW)) { - EXCHANGE.marketBuyOrdersNoThrow( + calldata = abi.encodeWithSelector( + EXCHANGE.marketBuyOrdersNoThrow.selector, orders, 0, signatures ); } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS)) { - EXCHANGE.marketSellOrders( + calldata = abi.encodeWithSelector( + EXCHANGE.marketSellOrders.selector, orders, 0, signatures ); } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS_NO_THROW)) { - EXCHANGE.marketSellOrdersNoThrow( + calldata = abi.encodeWithSelector( + EXCHANGE.marketSellOrdersNoThrow.selector, orders, 0, signatures ); } else if (currentFunctionId == uint8(ExchangeFunction.MATCH_ORDERS)) { - EXCHANGE.matchOrders( + calldata = abi.encodeWithSelector( + EXCHANGE.matchOrders.selector, order, order, signature, signature ); + } else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDER)) { + calldata = abi.encodeWithSelector( + EXCHANGE.cancelOrder.selector, + order + ); + } else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDERS_UP_TO)) { + calldata = abi.encodeWithSelector( + EXCHANGE.cancelOrdersUpTo.selector, + 0 + ); + } else if (currentFunctionId == uint8(ExchangeFunction.SET_SIGNATURE_VALIDATOR_APPROVAL)) { + calldata = abi.encodeWithSelector( + EXCHANGE.setSignatureValidatorApproval.selector, + address(0), + false + ); } + + // Call Exchange function, swallow error + address(EXCHANGE).call(calldata); + + // Revert reason is 100 bytes + bytes memory returnData = new bytes(100); + + // Copy return data + assembly { + returndatacopy(add(returnData, 32), 0, 100) + } + + // Revert if function reverted with REENTRANCY_ILLEGAL error + require(!REENTRANCY_ILLEGAL_REVERT_REASON.equals(returnData)); + + // Transfer will return true if function failed for any other reason return true; } } \ No newline at end of file -- cgit From 7d5a23969d5514ca3c942f11a48a8e858b9d0188 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 22 Aug 2018 18:01:45 -0700 Subject: Add reentrancy tests for fillOrder and wrapper functions --- packages/contracts/test/exchange/core.ts | 28 ++++ packages/contracts/test/exchange/wrapper.ts | 193 ++++++++++++++++++++++++++++ packages/contracts/test/utils/artifacts.ts | 2 + packages/contracts/test/utils/constants.ts | 16 +++ 4 files changed, 239 insertions(+) (limited to 'packages') diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index f9d8b7783..8ba55a0ab 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -14,6 +14,7 @@ import { DummyNoReturnERC20TokenContract } from '../../generated_contract_wrappe import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy'; import { ExchangeCancelEventArgs, ExchangeContract } from '../../generated_contract_wrappers/exchange'; +import { ReentrantERC20TokenContract } from '../../generated_contract_wrappers/reentrant_erc20_token'; import { TestStaticCallReceiverContract } from '../../generated_contract_wrappers/test_static_call_receiver'; import { artifacts } from '../utils/artifacts'; import { expectTransactionFailedAsync } from '../utils/assertions'; @@ -42,6 +43,7 @@ describe('Exchange core', () => { let zrxToken: DummyERC20TokenContract; let erc721Token: DummyERC721TokenContract; let noReturnErc20Token: DummyNoReturnERC20TokenContract; + let reentrantErc20Token: ReentrantERC20TokenContract; let exchange: ExchangeContract; let erc20Proxy: ERC20ProxyContract; let erc721Proxy: ERC721ProxyContract; @@ -117,6 +119,12 @@ describe('Exchange core', () => { provider, txDefaults, ); + reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync( + artifacts.ReentrantERC20Token, + provider, + txDefaults, + exchange.address, + ); defaultMakerAssetAddress = erc20TokenA.address; defaultTakerAssetAddress = erc20TokenB.address; @@ -144,6 +152,26 @@ describe('Exchange core', () => { signedOrder = await orderFactory.newSignedOrderAsync(); }); + const reentrancyTest = (functionNames: string[]) => { + _.forEach(functionNames, async (functionName: string, functionId: number) => { + const description = `should not allow fillOrder to reenter the Exchange contract via ${functionName}`; + it(description, async () => { + signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), + }); + await web3Wrapper.awaitTransactionSuccessAsync( + await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await expectTransactionFailedAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), + RevertReason.TransferFailed, + ); + }); + }); + }; + describe('fillOrder reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); + it('should throw if signature is invalid', async () => { signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), diff --git a/packages/contracts/test/exchange/wrapper.ts b/packages/contracts/test/exchange/wrapper.ts index d48441dca..aadb5ab59 100644 --- a/packages/contracts/test/exchange/wrapper.ts +++ b/packages/contracts/test/exchange/wrapper.ts @@ -11,6 +11,7 @@ import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dumm import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy'; import { ExchangeContract } from '../../generated_contract_wrappers/exchange'; +import { ReentrantERC20TokenContract } from '../../generated_contract_wrappers/reentrant_erc20_token'; import { artifacts } from '../utils/artifacts'; import { expectTransactionFailedAsync } from '../utils/assertions'; import { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from '../utils/block_timestamp'; @@ -40,6 +41,7 @@ describe('Exchange wrappers', () => { let exchange: ExchangeContract; let erc20Proxy: ERC20ProxyContract; let erc721Proxy: ERC721ProxyContract; + let reentrantErc20Token: ReentrantERC20TokenContract; let exchangeWrapper: ExchangeWrapper; let erc20Wrapper: ERC20Wrapper; @@ -104,6 +106,13 @@ describe('Exchange wrappers', () => { constants.AWAIT_TRANSACTION_MINED_MS, ); + reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync( + artifacts.ReentrantERC20Token, + provider, + txDefaults, + exchange.address, + ); + defaultMakerAssetAddress = erc20TokenA.address; defaultTakerAssetAddress = erc20TokenB.address; @@ -126,6 +135,26 @@ describe('Exchange wrappers', () => { await blockchainLifecycle.revertAsync(); }); describe('fillOrKillOrder', () => { + const reentrancyTest = (functionNames: string[]) => { + _.forEach(functionNames, async (functionName: string, functionId: number) => { + const description = `should not allow fillOrKillOrder to reenter the Exchange contract via ${functionName}`; + it(description, async () => { + const signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), + }); + await web3Wrapper.awaitTransactionSuccessAsync( + await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await expectTransactionFailedAsync( + exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress), + RevertReason.TransferFailed, + ); + }); + }); + }; + describe('fillOrKillOrder reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); + it('should transfer the correct amounts', async () => { const signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), @@ -197,6 +226,25 @@ describe('Exchange wrappers', () => { }); describe('fillOrderNoThrow', () => { + const reentrancyTest = (functionNames: string[]) => { + _.forEach(functionNames, async (functionName: string, functionId: number) => { + const description = `should not allow fillOrderNoThrow to reenter the Exchange contract via ${functionName}`; + it(description, async () => { + const signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), + }); + await web3Wrapper.awaitTransactionSuccessAsync( + await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress); + const newBalances = await erc20Wrapper.getBalancesAsync(); + expect(erc20Balances).to.deep.equal(newBalances); + }); + }); + }; + describe('fillOrderNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); + it('should transfer the correct amounts', async () => { const signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), @@ -397,6 +445,26 @@ describe('Exchange wrappers', () => { }); describe('batchFillOrders', () => { + const reentrancyTest = (functionNames: string[]) => { + _.forEach(functionNames, async (functionName: string, functionId: number) => { + const description = `should not allow batchFillOrders to reenter the Exchange contract via ${functionName}`; + it(description, async () => { + const signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), + }); + await web3Wrapper.awaitTransactionSuccessAsync( + await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await expectTransactionFailedAsync( + exchangeWrapper.batchFillOrdersAsync([signedOrder], takerAddress), + RevertReason.TransferFailed, + ); + }); + }); + }; + describe('batchFillOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); + it('should transfer the correct amounts', async () => { const takerAssetFillAmounts: BigNumber[] = []; const makerAssetAddress = erc20TokenA.address; @@ -446,6 +514,26 @@ describe('Exchange wrappers', () => { }); describe('batchFillOrKillOrders', () => { + const reentrancyTest = (functionNames: string[]) => { + _.forEach(functionNames, async (functionName: string, functionId: number) => { + const description = `should not allow batchFillOrKillOrders to reenter the Exchange contract via ${functionName}`; + it(description, async () => { + const signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), + }); + await web3Wrapper.awaitTransactionSuccessAsync( + await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await expectTransactionFailedAsync( + exchangeWrapper.batchFillOrKillOrdersAsync([signedOrder], takerAddress), + RevertReason.TransferFailed, + ); + }); + }); + }; + describe('batchFillOrKillOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); + it('should transfer the correct amounts', async () => { const takerAssetFillAmounts: BigNumber[] = []; const makerAssetAddress = erc20TokenA.address; @@ -512,6 +600,25 @@ describe('Exchange wrappers', () => { }); describe('batchFillOrdersNoThrow', async () => { + const reentrancyTest = (functionNames: string[]) => { + _.forEach(functionNames, async (functionName: string, functionId: number) => { + const description = `should not allow batchFillOrdersNoThrow to reenter the Exchange contract via ${functionName}`; + it(description, async () => { + const signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), + }); + await web3Wrapper.awaitTransactionSuccessAsync( + await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await exchangeWrapper.batchFillOrdersNoThrowAsync([signedOrder], takerAddress); + const newBalances = await erc20Wrapper.getBalancesAsync(); + expect(erc20Balances).to.deep.equal(newBalances); + }); + }); + }; + describe('batchFillOrdersNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); + it('should transfer the correct amounts', async () => { const takerAssetFillAmounts: BigNumber[] = []; const makerAssetAddress = erc20TokenA.address; @@ -625,6 +732,28 @@ describe('Exchange wrappers', () => { }); describe('marketSellOrders', () => { + const reentrancyTest = (functionNames: string[]) => { + _.forEach(functionNames, async (functionName: string, functionId: number) => { + const description = `should not allow marketSellOrders to reenter the Exchange contract via ${functionName}`; + it(description, async () => { + const signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), + }); + await web3Wrapper.awaitTransactionSuccessAsync( + await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await expectTransactionFailedAsync( + exchangeWrapper.marketSellOrdersAsync([signedOrder], takerAddress, { + takerAssetFillAmount: signedOrder.takerAssetAmount, + }), + RevertReason.TransferFailed, + ); + }); + }); + }; + describe('marketSellOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); + it('should stop when the entire takerAssetFillAmount is filled', async () => { const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus( signedOrders[1].takerAssetAmount.div(2), @@ -717,6 +846,27 @@ describe('Exchange wrappers', () => { }); describe('marketSellOrdersNoThrow', () => { + const reentrancyTest = (functionNames: string[]) => { + _.forEach(functionNames, async (functionName: string, functionId: number) => { + const description = `should not allow marketSellOrdersNoThrow to reenter the Exchange contract via ${functionName}`; + it(description, async () => { + const signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), + }); + await web3Wrapper.awaitTransactionSuccessAsync( + await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await exchangeWrapper.marketSellOrdersNoThrowAsync([signedOrder], takerAddress, { + takerAssetFillAmount: signedOrder.takerAssetAmount, + }); + const newBalances = await erc20Wrapper.getBalancesAsync(); + expect(erc20Balances).to.deep.equal(newBalances); + }); + }); + }; + describe('marketSellOrdersNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); + it('should stop when the entire takerAssetFillAmount is filled', async () => { const takerAssetFillAmount = signedOrders[0].takerAssetAmount.plus( signedOrders[1].takerAssetAmount.div(2), @@ -843,6 +993,28 @@ describe('Exchange wrappers', () => { }); describe('marketBuyOrders', () => { + const reentrancyTest = (functionNames: string[]) => { + _.forEach(functionNames, async (functionName: string, functionId: number) => { + const description = `should not allow marketBuyOrders to reenter the Exchange contract via ${functionName}`; + it(description, async () => { + const signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), + }); + await web3Wrapper.awaitTransactionSuccessAsync( + await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await expectTransactionFailedAsync( + exchangeWrapper.marketBuyOrdersAsync([signedOrder], takerAddress, { + makerAssetFillAmount: signedOrder.makerAssetAmount, + }), + RevertReason.TransferFailed, + ); + }); + }); + }; + describe('marketBuyOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); + it('should stop when the entire makerAssetFillAmount is filled', async () => { const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus( signedOrders[1].makerAssetAmount.div(2), @@ -933,6 +1105,27 @@ describe('Exchange wrappers', () => { }); describe('marketBuyOrdersNoThrow', () => { + const reentrancyTest = (functionNames: string[]) => { + _.forEach(functionNames, async (functionName: string, functionId: number) => { + const description = `should not allow marketBuyOrdersNoThrow to reenter the Exchange contract via ${functionName}`; + it(description, async () => { + const signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), + }); + await web3Wrapper.awaitTransactionSuccessAsync( + await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await exchangeWrapper.marketBuyOrdersNoThrowAsync([signedOrder], takerAddress, { + makerAssetFillAmount: signedOrder.makerAssetAmount, + }); + const newBalances = await erc20Wrapper.getBalancesAsync(); + expect(erc20Balances).to.deep.equal(newBalances); + }); + }); + }; + describe('marketBuyOrdersNoThrow reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); + it('should stop when the entire makerAssetFillAmount is filled', async () => { const makerAssetFillAmount = signedOrders[0].makerAssetAmount.plus( signedOrders[1].makerAssetAmount.div(2), diff --git a/packages/contracts/test/utils/artifacts.ts b/packages/contracts/test/utils/artifacts.ts index 2f6fcef71..5ddb5cc7f 100644 --- a/packages/contracts/test/utils/artifacts.ts +++ b/packages/contracts/test/utils/artifacts.ts @@ -16,6 +16,7 @@ import * as MixinAuthorizable from '../../artifacts/MixinAuthorizable.json'; import * as MultiSigWallet from '../../artifacts/MultiSigWallet.json'; import * as MultiSigWalletWithTimeLock from '../../artifacts/MultiSigWalletWithTimeLock.json'; import * as OrderValidator from '../../artifacts/OrderValidator.json'; +import * as ReentrantERC20Token from '../../artifacts/ReentrantERC20Token.json'; import * as TestAssetProxyDispatcher from '../../artifacts/TestAssetProxyDispatcher.json'; import * as TestAssetProxyOwner from '../../artifacts/TestAssetProxyOwner.json'; import * as TestConstants from '../../artifacts/TestConstants.json'; @@ -49,6 +50,7 @@ export const artifacts = { MultiSigWallet: (MultiSigWallet as any) as ContractArtifact, MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as ContractArtifact, OrderValidator: (OrderValidator as any) as ContractArtifact, + ReentrantERC20Token: (ReentrantERC20Token as any) as ContractArtifact, TestAssetProxyOwner: (TestAssetProxyOwner as any) as ContractArtifact, TestAssetProxyDispatcher: (TestAssetProxyDispatcher as any) as ContractArtifact, TestConstants: (TestConstants as any) as ContractArtifact, diff --git a/packages/contracts/test/utils/constants.ts b/packages/contracts/test/utils/constants.ts index 65eaee398..7060b5091 100644 --- a/packages/contracts/test/utils/constants.ts +++ b/packages/contracts/test/utils/constants.ts @@ -51,4 +51,20 @@ export const constants = { WORD_LENGTH: 32, ZERO_AMOUNT: new BigNumber(0), PERCENTAGE_DENOMINATOR: new BigNumber(10).pow(18), + FUNCTIONS_WITH_MUTEX: [ + 'FILL_ORDER', + 'FILL_OR_KILL_ORDER', + 'FILL_ORDER_NO_THROW', + 'BATCH_FILL_ORDERS', + 'BATCH_FILL_OR_KILL_ORDERS', + 'BATCH_FILL_ORDERS_NO_THROW', + 'MARKET_BUY_ORDERS', + 'MARKET_BUY_ORDERS_NO_THROW', + 'MARKET_SELL_ORDERS', + 'MARKET_SELL_ORDERS_NO_THROW', + 'MATCH_ORDERS', + 'CANCEL_ORDER', + 'CANCEL_ORDERS_UP_TO', + 'SET_SIGNATURE_VALIDATOR_APPROVAL', + ], }; -- cgit From a09ee907396d35468e0ad632fb990cb1c8870b49 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 22 Aug 2018 23:27:43 -0700 Subject: Add tests for matchOrders --- packages/contracts/test/exchange/core.ts | 2 +- packages/contracts/test/exchange/match_orders.ts | 173 +++++++---------------- 2 files changed, 53 insertions(+), 122 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index 8ba55a0ab..3bb71b58f 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -530,7 +530,7 @@ describe('Exchange core', () => { // HACK(albrow): We need to hardcode the gas estimate here because // the Geth gas estimator doesn't work with the way we use // delegatecall and swallow errors. - gas: 490000, + gas: 600000, }); const newBalances = await erc20Wrapper.getBalancesAsync(); diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index 46b3569bd..8cd873a85 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -11,6 +11,7 @@ import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dumm import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy'; import { ExchangeContract } from '../../generated_contract_wrappers/exchange'; +import { ReentrantERC20TokenContract } from '../../generated_contract_wrappers/reentrant_erc20_token'; import { artifacts } from '../utils/artifacts'; import { expectTransactionFailedAsync } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; @@ -39,6 +40,7 @@ describe('matchOrders', () => { let erc20TokenB: DummyERC20TokenContract; let zrxToken: DummyERC20TokenContract; let erc721Token: DummyERC721TokenContract; + let reentrantErc20Token: ReentrantERC20TokenContract; let exchange: ExchangeContract; let erc20Proxy: ERC20ProxyContract; let erc721Proxy: ERC721ProxyContract; @@ -127,21 +129,39 @@ describe('matchOrders', () => { }), constants.AWAIT_TRANSACTION_MINED_MS, ); + + reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync( + artifacts.ReentrantERC20Token, + provider, + txDefaults, + exchange.address, + ); + // Set default addresses defaultERC20MakerAssetAddress = erc20TokenA.address; defaultERC20TakerAssetAddress = erc20TokenB.address; defaultERC721AssetAddress = erc721Token.address; // Create default order parameters - const defaultOrderParams = { + const defaultOrderParamsLeft = { ...constants.STATIC_ORDER_PARAMS, + makerAddress: makerAddressLeft, exchangeAddress: exchange.address, makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + feeRecipientAddress: feeRecipientAddressLeft, + }; + const defaultOrderParamsRight = { + ...constants.STATIC_ORDER_PARAMS, + makerAddress: makerAddressRight, + exchangeAddress: exchange.address, + makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), + feeRecipientAddress: feeRecipientAddressRight, }; const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)]; - orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParams); + orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParamsLeft); const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)]; - orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParams); + orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParamsRight); // Set match order tester matchOrderTester = new MatchOrderTester(exchangeWrapper, erc20Wrapper, erc721Wrapper, zrxToken.address); }); @@ -157,21 +177,44 @@ describe('matchOrders', () => { erc721TokenIdsByOwner = await erc721Wrapper.getBalancesAsync(); }); + const reentrancyTest = (functionNames: string[]) => { + _.forEach(functionNames, async (functionName: string, functionId: number) => { + const description = `should not allow matchOrders to reenter the Exchange contract via ${functionName}`; + it(description, async () => { + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAddress: makerAddressRight, + takerAssetData: assetDataUtils.encodeERC20AssetData(reentrantErc20Token.address), + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + feeRecipientAddress: feeRecipientAddressRight, + }); + await web3Wrapper.awaitTransactionSuccessAsync( + await reentrantErc20Token.setCurrentFunction.sendTransactionAsync(functionId), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await expectTransactionFailedAsync( + exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), + RevertReason.TransferFailed, + ); + }); + }); + }; + describe('matchOrders reentrancy tests', () => reentrancyTest(constants.FUNCTIONS_WITH_MUTEX)); + it('should transfer the correct amounts when orders completely fill each other', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndVerifyBalancesAsync( @@ -192,18 +235,12 @@ describe('matchOrders', () => { it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Store original taker balance const takerInitialBalances = _.cloneDeep(erc20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress]); @@ -237,18 +274,12 @@ describe('matchOrders', () => { it('should transfer the correct amounts when left order is completely filled and right order is partially filled', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Match orders await matchOrderTester.matchOrdersAndVerifyBalancesAsync( @@ -269,18 +300,12 @@ describe('matchOrders', () => { it('should transfer the correct amounts when right order is completely filled and left order is partially filled', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Match orders await matchOrderTester.matchOrdersAndVerifyBalancesAsync( @@ -301,18 +326,12 @@ describe('matchOrders', () => { it('should transfer the correct amounts when consecutive calls are used to completely fill the left order', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Match orders let newERC20BalancesByOwner: ERC20BalancesByOwner; @@ -340,12 +359,8 @@ describe('matchOrders', () => { // However, we use 100/50 to ensure a partial fill as we want to go down the "left fill" // branch in the contract twice for this test. const signedOrderRight2 = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Match signedOrderLeft with signedOrderRight2 const leftTakerAssetFilledAmount = signedOrderRight.makerAssetAmount; @@ -370,19 +385,13 @@ describe('matchOrders', () => { it('should transfer the correct amounts when consecutive calls are used to completely fill the right order', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Match orders let newERC20BalancesByOwner: ERC20BalancesByOwner; @@ -410,10 +419,8 @@ describe('matchOrders', () => { // However, we use 100/50 to ensure a partial fill as we want to go down the "right fill" // branch in the contract twice for this test. const signedOrderLeft2 = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); // Match signedOrderLeft2 with signedOrderRight const leftTakerAssetFilledAmount = new BigNumber(0); @@ -441,15 +448,11 @@ describe('matchOrders', () => { it('should transfer the correct amounts if fee recipient is the same across both matched orders', async () => { const feeRecipientAddress = feeRecipientAddressLeft; const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), feeRecipientAddress, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feeRecipientAddress, @@ -467,18 +470,12 @@ describe('matchOrders', () => { it('should transfer the correct amounts if taker is also the left order maker', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Match orders takerAddress = signedOrderLeft.makerAddress; @@ -494,18 +491,12 @@ describe('matchOrders', () => { it('should transfer the correct amounts if taker is also the right order maker', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Match orders takerAddress = signedOrderRight.makerAddress; @@ -521,18 +512,12 @@ describe('matchOrders', () => { it('should transfer the correct amounts if taker is also the left fee recipient', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Match orders takerAddress = feeRecipientAddressLeft; @@ -548,18 +533,12 @@ describe('matchOrders', () => { it('should transfer the correct amounts if taker is also the right fee recipient', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Match orders takerAddress = feeRecipientAddressRight; @@ -575,18 +554,12 @@ describe('matchOrders', () => { it('should transfer the correct amounts if left maker is the left fee recipient and right maker is the right fee recipient', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress: makerAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress: makerAddressRight, }); // Match orders await matchOrderTester.matchOrdersAndVerifyBalancesAsync( @@ -601,18 +574,12 @@ describe('matchOrders', () => { it('Should throw if left order is not fillable', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Cancel left order await exchangeWrapper.cancelOrderAsync(signedOrderLeft, signedOrderLeft.makerAddress); @@ -626,18 +593,12 @@ describe('matchOrders', () => { it('Should throw if right order is not fillable', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Cancel right order await exchangeWrapper.cancelOrderAsync(signedOrderRight, signedOrderRight.makerAddress); @@ -651,18 +612,12 @@ describe('matchOrders', () => { it('should throw if there is not a positive spread', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Match orders return expectTransactionFailedAsync( @@ -674,18 +629,13 @@ describe('matchOrders', () => { it('should throw if the left maker asset is not equal to the right taker asset ', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Match orders return expectTransactionFailedAsync( @@ -701,20 +651,13 @@ describe('matchOrders', () => { it('should throw if the right maker asset is not equal to the left taker asset', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Match orders return expectTransactionFailedAsync( @@ -727,20 +670,14 @@ describe('matchOrders', () => { // Create orders to match const erc721TokenToTransfer = erc721LeftMakerAssetIds[0]; const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, makerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), makerAssetAmount: new BigNumber(1), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), takerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: new BigNumber(1), - feeRecipientAddress: feeRecipientAddressRight, }); // Match orders await matchOrderTester.matchOrdersAndVerifyBalancesAsync( @@ -762,20 +699,14 @@ describe('matchOrders', () => { // Create orders to match const erc721TokenToTransfer = erc721RightMakerAssetIds[0]; const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), takerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer), makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: new BigNumber(1), - feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, makerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), makerAssetAmount: new BigNumber(1), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - feeRecipientAddress: feeRecipientAddressRight, }); // Match orders await matchOrderTester.matchOrdersAndVerifyBalancesAsync( -- cgit From c28c3db63fb80d5d5e82ccd3b327cb8c408cc6fa Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Thu, 23 Aug 2018 16:43:46 -0700 Subject: Only use one nonReentrant modifier, remove modifier from fillOrderNoThrow variations --- .../2.0.0/protocol/Exchange/MixinExchangeCore.sol | 2 +- .../2.0.0/protocol/Exchange/MixinMatchOrders.sol | 2 +- .../protocol/Exchange/MixinWrapperFunctions.sol | 17 +++++------ .../ReentrantERC20Token/ReentrantERC20Token.sol | 33 +--------------------- .../utils/ReentrancyGuard/ReentrancyGuard.sol | 14 +-------- packages/contracts/test/utils/constants.ts | 4 --- 6 files changed, 11 insertions(+), 61 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index f02514fc6..291af3792 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -89,7 +89,7 @@ contract MixinExchangeCore is bytes memory signature ) public - lockMutex + nonReentrant returns (FillResults memory fillResults) { fillResults = fillOrderInternal( diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index 39c47e6f9..25220a673 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -50,7 +50,7 @@ contract MixinMatchOrders is bytes memory rightSignature ) public - lockMutex + nonReentrant returns (LibFillResults.MatchedFillResults memory matchedFillResults) { // We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData. diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol index b0474b110..39fa724cc 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol @@ -33,7 +33,8 @@ contract MixinWrapperFunctions is LibMath, LibFillResults, LibAbiEncoder, - MExchangeCore + MExchangeCore, + MWrapperFunctions { /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. @@ -46,7 +47,7 @@ contract MixinWrapperFunctions is bytes memory signature ) public - lockMutex + nonReentrant returns (FillResults memory fillResults) { fillResults = fillOrKillOrderInternal( @@ -69,7 +70,6 @@ contract MixinWrapperFunctions is bytes memory signature ) public - nonReentrant returns (FillResults memory fillResults) { // ABI encode calldata for `fillOrder` @@ -111,7 +111,7 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public - lockMutex + nonReentrant returns (FillResults memory totalFillResults) { uint256 ordersLength = orders.length; @@ -138,7 +138,7 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public - lockMutex + nonReentrant returns (FillResults memory totalFillResults) { uint256 ordersLength = orders.length; @@ -166,7 +166,6 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public - nonReentrant returns (FillResults memory totalFillResults) { uint256 ordersLength = orders.length; @@ -192,7 +191,7 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public - lockMutex + nonReentrant returns (FillResults memory totalFillResults) { bytes memory takerAssetData = orders[0].takerAssetData; @@ -237,7 +236,6 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public - nonReentrant returns (FillResults memory totalFillResults) { bytes memory takerAssetData = orders[0].takerAssetData; @@ -281,7 +279,7 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public - lockMutex + nonReentrant returns (FillResults memory totalFillResults) { bytes memory makerAssetData = orders[0].makerAssetData; @@ -334,7 +332,6 @@ contract MixinWrapperFunctions is bytes[] memory signatures ) public - nonReentrant returns (FillResults memory totalFillResults) { bytes memory makerAssetData = orders[0].makerAssetData; diff --git a/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol b/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol index 8d575d09a..8bfdd2e66 100644 --- a/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol +++ b/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol @@ -40,17 +40,14 @@ contract ReentrantERC20Token is ); // All of these functions are potentially vulnerable to reentrancy + // We do not test any "noThrow" functions because `fillOrderNoThrow` makes a delegatecall to `fillOrder` enum ExchangeFunction { FILL_ORDER, FILL_OR_KILL_ORDER, - FILL_ORDER_NO_THROW, BATCH_FILL_ORDERS, BATCH_FILL_OR_KILL_ORDERS, - BATCH_FILL_ORDERS_NO_THROW, MARKET_BUY_ORDERS, - MARKET_BUY_ORDERS_NO_THROW, MARKET_SELL_ORDERS, - MARKET_SELL_ORDERS_NO_THROW, MATCH_ORDERS, CANCEL_ORDER, CANCEL_ORDERS_UP_TO, @@ -111,13 +108,6 @@ contract ReentrantERC20Token is 0, signature ); - } else if (currentFunctionId == uint8(ExchangeFunction.FILL_ORDER_NO_THROW)) { - calldata = abi.encodeWithSelector( - EXCHANGE.fillOrderNoThrow.selector, - order, - 0, - signature - ); } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS)) { calldata = abi.encodeWithSelector( EXCHANGE.batchFillOrders.selector, @@ -132,13 +122,6 @@ contract ReentrantERC20Token is takerAssetFillAmounts, signatures ); - } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS_NO_THROW)) { - calldata = abi.encodeWithSelector( - EXCHANGE.batchFillOrdersNoThrow.selector, - orders, - takerAssetFillAmounts, - signatures - ); } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS)) { calldata = abi.encodeWithSelector( EXCHANGE.marketBuyOrders.selector, @@ -146,13 +129,6 @@ contract ReentrantERC20Token is 0, signatures ); - } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS_NO_THROW)) { - calldata = abi.encodeWithSelector( - EXCHANGE.marketBuyOrdersNoThrow.selector, - orders, - 0, - signatures - ); } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS)) { calldata = abi.encodeWithSelector( EXCHANGE.marketSellOrders.selector, @@ -160,13 +136,6 @@ contract ReentrantERC20Token is 0, signatures ); - } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS_NO_THROW)) { - calldata = abi.encodeWithSelector( - EXCHANGE.marketSellOrdersNoThrow.selector, - orders, - 0, - signatures - ); } else if (currentFunctionId == uint8(ExchangeFunction.MATCH_ORDERS)) { calldata = abi.encodeWithSelector( EXCHANGE.matchOrders.selector, diff --git a/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol b/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol index 2b980c7ca..1dee512d4 100644 --- a/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol +++ b/packages/contracts/src/2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol @@ -23,21 +23,9 @@ contract ReentrancyGuard { // Locked state of mutex bool private locked = false; - /// @dev Functions with this modifier cannot be reentered. - modifier nonReentrant() { - // Ensure mutex is unlocked - require( - !locked, - "REENTRANCY_ILLEGAL" - ); - - // Perform function call - _; - } - /// @dev Functions with this modifer cannot be reentered. The mutex will be locked /// before function execution and unlocked after. - modifier lockMutex() { + modifier nonReentrant() { // Ensure mutex is unlocked require( !locked, diff --git a/packages/contracts/test/utils/constants.ts b/packages/contracts/test/utils/constants.ts index 7060b5091..ee4378d2e 100644 --- a/packages/contracts/test/utils/constants.ts +++ b/packages/contracts/test/utils/constants.ts @@ -54,14 +54,10 @@ export const constants = { FUNCTIONS_WITH_MUTEX: [ 'FILL_ORDER', 'FILL_OR_KILL_ORDER', - 'FILL_ORDER_NO_THROW', 'BATCH_FILL_ORDERS', 'BATCH_FILL_OR_KILL_ORDERS', - 'BATCH_FILL_ORDERS_NO_THROW', 'MARKET_BUY_ORDERS', - 'MARKET_BUY_ORDERS_NO_THROW', 'MARKET_SELL_ORDERS', - 'MARKET_SELL_ORDERS_NO_THROW', 'MATCH_ORDERS', 'CANCEL_ORDER', 'CANCEL_ORDERS_UP_TO', -- cgit From c8500cab10b8ffd175acd760cbd1286577a1c4b1 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 24 Aug 2018 16:35:43 -0700 Subject: Fix build --- packages/contracts/package.json | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'packages') diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 8d75eb4ce..1c1ac81da 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -20,11 +20,14 @@ "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov", "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", - "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", + "run_mocha": + "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", "compile": "sol-compiler --contracts-dir src", "clean": "shx rm -rf lib generated_contract_wrappers", - "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output generated_contract_wrappers --backend ethers", - "lint": "tslint --project . --exclude **/src/generated_contract_wrappers/**/* --exclude **/lib/**/* && yarn lint-contracts", + "generate_contract_wrappers": + "abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output generated_contract_wrappers --backend ethers", + "lint": + "tslint --project . --exclude **/src/generated_contract_wrappers/**/* --exclude **/lib/**/* && yarn lint-contracts", "coverage:report:text": "istanbul report text", "coverage:report:html": "istanbul report html && open coverage/index.html", "profiler:report:html": "istanbul report html && open coverage/index.html", @@ -34,7 +37,7 @@ }, "config": { "abis": - "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json" + "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json" }, "repository": { "type": "git", -- cgit From 407f63ef2055e8cd01af010a2e6d3a6c548c9793 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 20 Aug 2018 13:13:38 -0700 Subject: Removed calculateFillResults from matchOrders workflow. Eliminates compounded rounding errors. --- .../2.0.0/protocol/Exchange/MixinMatchOrders.sol | 67 ++++++++++------------ 1 file changed, 29 insertions(+), 38 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index 25220a673..2012aa6db 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -109,7 +109,7 @@ contract MixinMatchOrders is rightOrderInfo.orderTakerAssetFilledAmount, matchedFillResults.right ); - + // Settle matched orders. Succeeds or throws. settleMatchedOrders( leftOrder, @@ -169,52 +169,43 @@ contract MixinMatchOrders is // The amount saved by the left maker goes to the taker. // Either the left or right order will be fully filled; possibly both. // The left order is fully filled iff the right order can sell more than left can buy. - // That is: the amount required to fill the left order is less than or equal to - // the amount we can spend from the right order: - // <= * - // <= * / - // * <= * + + // Derive maker asset amounts for left & right orders, given store taker assert amounts uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount); + uint256 leftMakerAssetAmountRemaining = getPartialAmountFloor( + leftOrder.makerAssetAmount, + leftOrder.takerAssetAmount, + leftTakerAssetAmountRemaining + ); uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount); - uint256 leftTakerAssetFilledAmount; - uint256 rightTakerAssetFilledAmount; - if ( - safeMul(leftTakerAssetAmountRemaining, rightOrder.takerAssetAmount) <= - safeMul(rightTakerAssetAmountRemaining, rightOrder.makerAssetAmount) - ) { - // Left order will be fully filled: maximally fill left - leftTakerAssetFilledAmount = leftTakerAssetAmountRemaining; + uint256 rightMakerAssetAmountRemaining = getPartialAmountFloor( + rightOrder.makerAssetAmount, + rightOrder.takerAssetAmount, + rightTakerAssetAmountRemaining + ); - // The right order receives an amount proportional to how much was spent. - rightTakerAssetFilledAmount = getPartialAmountFloor( - rightOrder.takerAssetAmount, - rightOrder.makerAssetAmount, - leftTakerAssetFilledAmount + if (leftTakerAssetAmountRemaining >= rightMakerAssetAmountRemaining) { + // Case 1: Right order is fully filled: maximally fill right + matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining; + matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining; + matchedFillResults.left.makerAssetFilledAmount = getPartialAmountFloor( + leftOrder.makerAssetAmount, + leftOrder.takerAssetAmount, + matchedFillResults.right.makerAssetFilledAmount ); + matchedFillResults.left.takerAssetFilledAmount = matchedFillResults.right.makerAssetFilledAmount; } else { - // Right order will be fully filled: maximally fill right - rightTakerAssetFilledAmount = rightTakerAssetAmountRemaining; - - // The left order receives an amount proportional to how much was spent. - leftTakerAssetFilledAmount = getPartialAmountFloor( - rightOrder.makerAssetAmount, + // Case 2: Left order is fully filled: maximall fill left + matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining; + matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining; + matchedFillResults.right.makerAssetFilledAmount = matchedFillResults.left.takerAssetFilledAmount; + matchedFillResults.right.takerAssetFilledAmount = getPartialAmountCeil( rightOrder.takerAssetAmount, - rightTakerAssetFilledAmount + rightOrder.makerAssetAmount, + matchedFillResults.left.takerAssetFilledAmount ); } - // Calculate fill results for left order - matchedFillResults.left = calculateFillResults( - leftOrder, - leftTakerAssetFilledAmount - ); - - // Calculate fill results for right order - matchedFillResults.right = calculateFillResults( - rightOrder, - rightTakerAssetFilledAmount - ); - // Calculate amount given to taker matchedFillResults.leftMakerAssetSpreadAmount = safeSub( matchedFillResults.left.makerAssetFilledAmount, -- cgit From 057891b342cf639b493adcf0a136f7ee421aaaf9 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 20 Aug 2018 13:22:21 -0700 Subject: Added fees to matchOrders (previously in calculateFillResults --- .../2.0.0/protocol/Exchange/MixinMatchOrders.sol | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index 2012aa6db..58f131819 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -212,6 +212,30 @@ contract MixinMatchOrders is matchedFillResults.right.takerAssetFilledAmount ); + // Compute fees for left order + matchedFillResults.left.makerFeePaid = getPartialAmount( + matchedFillResults.left.takerAssetFilledAmount, + leftOrder.takerAssetAmount, + leftOrder.makerFee + ); + matchedFillResults.left.takerFeePaid = getPartialAmount( + matchedFillResults.left.takerAssetFilledAmount, + leftOrder.takerAssetAmount, + leftOrder.takerFee + ); + + // Compute fees for right order + matchedFillResults.right.makerFeePaid = getPartialAmount( + matchedFillResults.right.takerAssetFilledAmount, + rightOrder.takerAssetAmount, + rightOrder.makerFee + ); + matchedFillResults.right.takerFeePaid = getPartialAmount( + matchedFillResults.right.takerAssetFilledAmount, + rightOrder.takerAssetAmount, + rightOrder.takerFee + ); + // Return fill results return matchedFillResults; } -- cgit From 0ecdf1e2132141b199fa929ec1c6ca9979f06302 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 20 Aug 2018 14:02:40 -0700 Subject: All existing tests pass. --- packages/contracts/test/exchange/match_orders.ts | 105 ++++++++++++++++++++- .../contracts/test/utils/match_order_tester.ts | 53 +++++++---- 2 files changed, 137 insertions(+), 21 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index 8cd873a85..78f33ec7c 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -28,7 +28,7 @@ chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -describe('matchOrders', () => { +describe.only('matchOrders', () => { let makerAddressLeft: string; let makerAddressRight: string; let owner: string; @@ -176,6 +176,109 @@ describe('matchOrders', () => { erc20BalancesByOwner = await erc20Wrapper.getBalancesAsync(); erc721TokenIdsByOwner = await erc721Wrapper.getBalancesAsync(); }); + + + /* + it.only('should transfer the correct amounts when orders completely fill each other', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAddress: makerAddressLeft, + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2000), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1001), 0), + feeRecipientAddress: feeRecipientAddressLeft, + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAddress: makerAddressRight, + makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(3000), 0), + feeRecipientAddress: feeRecipientAddressRight, + }); + // Match signedOrderLeft with signedOrderRight + await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + signedOrderLeft, + signedOrderRight, + takerAddress, + erc20BalancesByOwner, + erc721TokenIdsByOwner, + ); + // // Verify left order was fully filled + // const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); + // expect(leftOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); + // Verify right order was fully filled + // const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); + // expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); + }); + */ + + it.only('Jacobs Example', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAddress: makerAddressLeft, + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), + feeRecipientAddress: feeRecipientAddressLeft, + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAddress: makerAddressRight, + makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 0), + feeRecipientAddress: feeRecipientAddressRight, + }); + // Match signedOrderLeft with signedOrderRight + await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + signedOrderLeft, + signedOrderRight, + takerAddress, + erc20BalancesByOwner, + erc721TokenIdsByOwner, + ); + // // Verify left order was fully filled + const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); + //expect(leftOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); + console.log("*** LEFT ORDER INFO ***\n", JSON.stringify(leftOrderInfo), "\n***************"); + // Verify right order was fully filled + const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); + // expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); + console.log("*** RIGHT ORDER INFO ***\n", JSON.stringify(rightOrderInfo), "\n***************"); + }); + + + /* + it.only('Jacobs Example', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAddress: makerAddressLeft, + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), + feeRecipientAddress: feeRecipientAddressLeft, + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAddress: makerAddressRight, + makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), + feeRecipientAddress: feeRecipientAddressRight, + }); + // Match signedOrderLeft with signedOrderRight + await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + signedOrderLeft, + signedOrderRight, + takerAddress, + erc20BalancesByOwner, + erc721TokenIdsByOwner, + ); + // // Verify left order was fully filled + // const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); + // expect(leftOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); + // Verify right order was fully filled + // const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); + // expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); + });*/ const reentrancyTest = (functionNames: string[]) => { _.forEach(functionNames, async (functionName: string, functionId: number) => { diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index fa2eabc12..ded429322 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -9,6 +9,7 @@ import { ERC20Wrapper } from './erc20_wrapper'; import { ERC721Wrapper } from './erc721_wrapper'; import { ExchangeWrapper } from './exchange_wrapper'; import { ERC20BalancesByOwner, ERC721TokenIdsByOwner, TransferAmountsByMatchOrders as TransferAmounts } from './types'; +import { TransactionReceiptWithDecodedLogs } from '../../../../node_modules/ethereum-types'; chaiSetup.configure(); const expect = chai.expect; @@ -108,7 +109,7 @@ export class MatchOrderTester { : new BigNumber(0); expect(expectedOrderFilledAmountRight).to.be.bignumber.equal(orderTakerAssetFilledAmountRight); // Match left & right orders - await this._exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress); + const transactionReceipt = await this._exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress); const newERC20BalancesByOwner = await this._erc20Wrapper.getBalancesAsync(); const newERC721TokenIdsByOwner = await this._erc721Wrapper.getBalancesAsync(); // Calculate expected balance changes @@ -117,6 +118,8 @@ export class MatchOrderTester { signedOrderRight, orderTakerAssetFilledAmountLeft, orderTakerAssetFilledAmountRight, + transactionReceipt, + takerAddress ); let expectedERC20BalancesByOwner: ERC20BalancesByOwner; let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner; @@ -135,6 +138,7 @@ export class MatchOrderTester { expectedERC721TokenIdsByOwner, newERC721TokenIdsByOwner, ); + expect(didExpectedBalancesMatchRealBalances).to.be.true(); return [newERC20BalancesByOwner, newERC721TokenIdsByOwner]; } @@ -150,26 +154,35 @@ export class MatchOrderTester { signedOrderRight: SignedOrder, orderTakerAssetFilledAmountLeft: BigNumber, orderTakerAssetFilledAmountRight: BigNumber, + transactionReceipt: TransactionReceiptWithDecodedLogs, + takerAddress: string ): Promise { - let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrderLeft), - ); - amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(orderTakerAssetFilledAmountLeft); - const amountSoldByLeftMaker = amountBoughtByLeftMaker - .times(signedOrderLeft.makerAssetAmount) - .dividedToIntegerBy(signedOrderLeft.takerAssetAmount); - const amountReceivedByRightMaker = amountBoughtByLeftMaker - .times(signedOrderRight.takerAssetAmount) - .dividedToIntegerBy(signedOrderRight.makerAssetAmount); - const amountReceivedByTaker = amountSoldByLeftMaker.minus(amountReceivedByRightMaker); - let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrderRight), - ); - amountBoughtByRightMaker = amountBoughtByRightMaker.minus(orderTakerAssetFilledAmountRight); - const amountSoldByRightMaker = amountBoughtByRightMaker - .times(signedOrderRight.makerAssetAmount) - .dividedToIntegerBy(signedOrderRight.takerAssetAmount); - const amountReceivedByLeftMaker = amountSoldByRightMaker; + // Parse logs + expect(transactionReceipt.logs.length).to.be.equal(2); + // First log is for left fill + const leftLog = ((transactionReceipt.logs[0] as any) as {args: { makerAddress: string, takerAddress: string, makerAssetFilledAmount: string, takerAssetFilledAmount: string, makerFeePaid: string, takerFeePaid: string}}); + expect(leftLog.args.makerAddress).to.be.equal(signedOrderLeft.makerAddress); + expect(leftLog.args.takerAddress).to.be.equal(takerAddress); + const amountBoughtByLeftMaker = new BigNumber(leftLog.args.takerAssetFilledAmount); + const amountSoldByLeftMaker = new BigNumber(leftLog.args.makerAssetFilledAmount); + // Second log is for right fill + const rightLog = ((transactionReceipt.logs[1] as any) as {args: { makerAddress: string, takerAddress: string, makerAssetFilledAmount: string, takerAssetFilledAmount: string, makerFeePaid: string, takerFeePaid: string}}); + expect(rightLog.args.makerAddress).to.be.equal(signedOrderRight.makerAddress); + expect(rightLog.args.takerAddress).to.be.equal(takerAddress); + const amountBoughtByRightMaker = new BigNumber(rightLog.args.takerAssetFilledAmount); + const amountSoldByRightMaker = new BigNumber(rightLog.args.makerAssetFilledAmount); + // Determine amount received by taker + const amountReceivedByTaker = amountSoldByLeftMaker.sub(amountBoughtByRightMaker); + + const amountReceivedByLeftMaker = amountBoughtByLeftMaker; + const amountReceivedByRightMaker = amountBoughtByRightMaker; + + console.log("Amount bought by left maker = ", JSON.stringify(amountBoughtByLeftMaker)); + console.log("Amount sold by left maker = ", JSON.stringify(amountSoldByLeftMaker)); + console.log("Amount bought by right maker = ", JSON.stringify(amountBoughtByRightMaker)); + console.log("Amount sold by right maker = ", JSON.stringify(amountSoldByRightMaker)); + console.log("Amount received by taker = ", JSON.stringify(amountReceivedByTaker)); + //const amountReceivedByLeftMaker = amountSoldByRightMaker; const feePaidByLeftMaker = signedOrderLeft.makerFee .times(amountSoldByLeftMaker) .dividedToIntegerBy(signedOrderLeft.makerAssetAmount); -- cgit From a32b201afe6c90a6bd06b88a5d512408cd3cb032 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 20 Aug 2018 17:00:08 -0700 Subject: Rounding for fees in match orders addressed, plus example --- .../2.0.0/protocol/Exchange/MixinMatchOrders.sol | 8 +-- .../contracts/test/utils/match_order_tester.ts | 84 ++++++++++++++++++++-- 2 files changed, 83 insertions(+), 9 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index 58f131819..b3f376eb8 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -214,8 +214,8 @@ contract MixinMatchOrders is // Compute fees for left order matchedFillResults.left.makerFeePaid = getPartialAmount( - matchedFillResults.left.takerAssetFilledAmount, - leftOrder.takerAssetAmount, + matchedFillResults.left.makerAssetFilledAmount, + leftOrder.makerAssetAmount, leftOrder.makerFee ); matchedFillResults.left.takerFeePaid = getPartialAmount( @@ -226,8 +226,8 @@ contract MixinMatchOrders is // Compute fees for right order matchedFillResults.right.makerFeePaid = getPartialAmount( - matchedFillResults.right.takerAssetFilledAmount, - rightOrder.takerAssetAmount, + matchedFillResults.right.makerAssetFilledAmount, + rightOrder.makerAssetAmount, rightOrder.makerFee ); matchedFillResults.right.takerFeePaid = getPartialAmount( diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index ded429322..5160b47d3 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -139,7 +139,78 @@ export class MatchOrderTester { newERC721TokenIdsByOwner, ); + // Compute actual transfer amounts + let actualTransferAmounts = {}; + const makerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.makerAssetData); + if (makerAssetProxyIdLeft === AssetProxyId.ERC20) { + const erc20AssetData = assetDataUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData); + const makerAssetAddressLeft = erc20AssetData.tokenAddress; + actualTransferAmounts.amountSoldByLeftMaker = erc20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sub(newERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]); + actualTransferAmounts.amountBoughtByRightMaker = newERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sub(erc20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft]); + actualTransferAmounts.amountReceivedByRightMaker = actualTransferAmounts.amountBoughtByRightMaker; + actualTransferAmounts.amountReceivedByTaker = newERC20BalancesByOwner[takerAddress][makerAssetAddressLeft].sub(erc20BalancesByOwner[takerAddress][makerAssetAddressLeft]); + } else if(makerAssetProxyIdLeft === AssetProxyId.ERC721) { + } + const makerAssetProxyIdRight = assetDataUtils.decodeAssetProxyId(signedOrderRight.makerAssetData); + if (makerAssetProxyIdRight === AssetProxyId.ERC20) { + const erc20AssetData = assetDataUtils.decodeERC20AssetData(signedOrderRight.makerAssetData); + const makerAssetAddressRight = erc20AssetData.tokenAddress; + actualTransferAmounts.amountSoldByRightMaker = erc20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight].sub(newERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]); + actualTransferAmounts.amountBoughtByLeftMaker = newERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sub(erc20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]); + actualTransferAmounts.amountReceivedByLeftMaker = actualTransferAmounts.amountBoughtByLeftMaker; + } else if(makerAssetProxyIdRight === AssetProxyId.ERC721) { + } + // Fees + actualTransferAmounts.feePaidByLeftMaker = erc20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress].sub(newERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]); + actualTransferAmounts.feePaidByRightMaker = erc20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress].sub(newERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]); + actualTransferAmounts.totalFeePaidByTaker = erc20BalancesByOwner[takerAddress][this._feeTokenAddress].sub(newERC20BalancesByOwner[takerAddress][this._feeTokenAddress]); + + console.log("amountBoughtByLeftMaker"); + expect(expectedTransferAmounts.amountBoughtByLeftMaker).to.be.bignumber.equal(actualTransferAmounts.amountBoughtByLeftMaker); + console.log("amountSoldByLeftMaker"); + expect(expectedTransferAmounts.amountSoldByLeftMaker).to.be.bignumber.equal(actualTransferAmounts.amountSoldByLeftMaker); + console.log("amountBoughtByRightMaker"); + expect(expectedTransferAmounts.amountBoughtByRightMaker).to.be.bignumber.equal(actualTransferAmounts.amountBoughtByRightMaker); + console.log("amountSoldByRightMaker"); + expect(expectedTransferAmounts.amountSoldByRightMaker).to.be.bignumber.equal(actualTransferAmounts.amountSoldByRightMaker); + console.log("amountReceivedByTaker"); + expect(expectedTransferAmounts.amountReceivedByTaker).to.be.bignumber.equal(actualTransferAmounts.amountReceivedByTaker); + console.log("feePaidByLeftMaker"); + expect(expectedTransferAmounts.feePaidByLeftMaker).to.be.bignumber.equal(actualTransferAmounts.feePaidByLeftMaker); + console.log("feePaidByRightMaker"); + expect(expectedTransferAmounts.feePaidByRightMaker).to.be.bignumber.equal(actualTransferAmounts.feePaidByRightMaker); + console.log("totalFeePaidByTaker"); + expect(expectedTransferAmounts.totalFeePaidByTaker).to.be.bignumber.equal(actualTransferAmounts.totalFeePaidByTaker); + + + +/* + const actualTransferAmounts = { + // Left Maker + amountBoughtByLeftMaker, + amountSoldByLeftMaker, + amountReceivedByLeftMaker, + feePaidByLeftMaker, + // Right Maker + amountBoughtByRightMaker, + amountSoldByRightMaker, + amountReceivedByRightMaker, + feePaidByRightMaker, + // Taker + amountReceivedByTaker, + feePaidByTakerLeft, + feePaidByTakerRight, + totalFeePaidByTaker, + // Fee Recipients + feeReceivedLeft, + feeReceivedRight, + };*/ + + // This is a catch-all to ensure that no other balances changed + console.log("Catch-all"); expect(didExpectedBalancesMatchRealBalances).to.be.true(); + + return [newERC20BalancesByOwner, newERC721TokenIdsByOwner]; } /// @dev Calculates expected transfer amounts between order makers, fee recipients, and @@ -180,7 +251,7 @@ export class MatchOrderTester { console.log("Amount bought by left maker = ", JSON.stringify(amountBoughtByLeftMaker)); console.log("Amount sold by left maker = ", JSON.stringify(amountSoldByLeftMaker)); console.log("Amount bought by right maker = ", JSON.stringify(amountBoughtByRightMaker)); - console.log("Amount sold by right maker = ", JSON.stringify(amountSoldByRightMaker)); + console.log("Amount sold by right maker = ", JSON.stringify(amountSoldByRightMaker)); console.log("Amount received by taker = ", JSON.stringify(amountReceivedByTaker)); //const amountReceivedByLeftMaker = amountSoldByRightMaker; const feePaidByLeftMaker = signedOrderLeft.makerFee @@ -189,12 +260,15 @@ export class MatchOrderTester { const feePaidByRightMaker = signedOrderRight.makerFee .times(amountSoldByRightMaker) .dividedToIntegerBy(signedOrderRight.makerAssetAmount); + const feePaidByTakerLeft = signedOrderLeft.takerFee - .times(amountSoldByLeftMaker) - .dividedToIntegerBy(signedOrderLeft.makerAssetAmount); + .times(amountBoughtByLeftMaker) + .dividedToIntegerBy(signedOrderLeft.takerAssetAmount); + + const feePaidByTakerRight = signedOrderRight.takerFee - .times(amountSoldByRightMaker) - .dividedToIntegerBy(signedOrderRight.makerAssetAmount); + .times(amountBoughtByRightMaker) + .dividedToIntegerBy(signedOrderRight.takerAssetAmount); const totalFeePaidByTaker = feePaidByTakerLeft.add(feePaidByTakerRight); const feeReceivedLeft = feePaidByLeftMaker.add(feePaidByTakerLeft); const feeReceivedRight = feePaidByRightMaker.add(feePaidByTakerRight); -- cgit From ca5c9e77c0f068cf653afc9c8b61bdc25318f8e2 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 21 Aug 2018 14:45:39 -0700 Subject: Ironing out the new set of test cases for order matchubng --- packages/contracts/test/exchange/match_orders.ts | 2 +- .../contracts/test/utils/match_order_tester.ts | 111 +++++++++++++++++++-- 2 files changed, 102 insertions(+), 11 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index 78f33ec7c..b2ffa31b2 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -212,7 +212,7 @@ describe.only('matchOrders', () => { }); */ - it.only('Jacobs Example', async () => { + it('Jacobs Example', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ makerAddress: makerAddressLeft, diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index 5160b47d3..f9e45319a 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -57,6 +57,27 @@ export class MatchOrderTester { ); return doesErc721TokenIdsMatch; } + + /* + private static compareTokenIdLists(list1: BigNumber[], list2: BigNumber[]) { + // ERC721 Token Ids + list1 = _.(list1); + _.sortBy(tokenIds); + }); + }, + ); + const sortedNewERC721TokenIdsByOwner = _.mapValues(realERC721TokenIdsByOwner, tokenIdsByOwner => { + _.mapValues(tokenIdsByOwner, tokenIds => { + _.sortBy(tokenIds); + }); + }); + const doesErc721TokenIdsMatch = _.isEqual( + sortedExpectedNewERC721TokenIdsByOwner, + sortedNewERC721TokenIdsByOwner, + ); + return doesErc721TokenIdsMatch; + }*/ + /// @dev Constructs new MatchOrderTester. /// @param exchangeWrapper Used to call to the Exchange. /// @param erc20Wrapper Used to fetch ERC20 balances. @@ -139,6 +160,68 @@ export class MatchOrderTester { newERC721TokenIdsByOwner, ); + // Individual balance comparisons + const makerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.makerAssetData); + const makerERC20AssetDataLeft = makerAssetProxyIdLeft == AssetProxyId.ERC20 ? assetDataUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData) : assetDataUtils.decodeERC721AssetData(signedOrderLeft.makerAssetData); + const makerAssetAddressLeft = makerERC20AssetDataLeft.tokenAddress; + const makerAssetProxyIdRight = assetDataUtils.decodeAssetProxyId(signedOrderRight.makerAssetData); + const makerERC20AssetDataRight = makerAssetProxyIdRight == AssetProxyId.ERC20 ? assetDataUtils.decodeERC20AssetData(signedOrderRight.makerAssetData) : assetDataUtils.decodeERC721AssetData(signedOrderRight.makerAssetData); + const makerAssetAddressRight = makerERC20AssetDataRight.tokenAddress; + + console.log("Left Maker: Sell Amount"); + if(makerAssetProxyIdLeft == AssetProxyId.ERC20) { + expect(newERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]); + } else if(makerAssetProxyIdLeft == AssetProxyId.ERC721) { + expect(newERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort()).to.be.deep.equal(newERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort()); + } else { + throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`); + } + + console.log("Left Maker: Buy Amount"); + if(makerAssetProxyIdRight == AssetProxyId.ERC20) { + expect(newERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]); + } else if(makerAssetProxyIdRight == AssetProxyId.ERC721) { + expect(newERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort()).to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort()); + } else { + throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdRight}`); + } + + console.log("Left Maker: Fees"); + expect(newERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]); + + console.log("Right Maker: Sell Amount"); + if(makerAssetProxyIdRight == AssetProxyId.ERC20) { + expect(newERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]); + } else if(makerAssetProxyIdRight == AssetProxyId.ERC721) { + expect(newERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]).to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]); + } else { + throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdRight}`); + } + + console.log("Right Maker: Buy Amount"); + if(makerAssetProxyIdLeft == AssetProxyId.ERC20) { + expect(newERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft]).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft]); + } else if(makerAssetProxyIdLeft == AssetProxyId.ERC721) { + expect(newERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort()).to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort()); + } else { + throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`); + } + console.log("Right Maker: Fees"); + expect(newERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]); + console.log("Taker: Receive Amount"); + if(makerAssetProxyIdLeft == AssetProxyId.ERC20) { + expect(newERC20BalancesByOwner[takerAddress][makerAssetAddressLeft]).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][makerAssetAddressLeft]); + } else if(makerAssetProxyIdLeft == AssetProxyId.ERC721) { + expect(newERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort()).to.be.deep.equal(expectedERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort()); + } else { + throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`); + } + console.log("Taker: Fees"); + expect(newERC20BalancesByOwner[takerAddress][this._feeTokenAddress]).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][this._feeTokenAddress]); + console.log("Balance catch-all"); + expect(didExpectedBalancesMatchRealBalances).to.be.true(); + + /* // Compute actual transfer amounts let actualTransferAmounts = {}; const makerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.makerAssetData); @@ -163,24 +246,24 @@ export class MatchOrderTester { // Fees actualTransferAmounts.feePaidByLeftMaker = erc20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress].sub(newERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]); actualTransferAmounts.feePaidByRightMaker = erc20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress].sub(newERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]); - actualTransferAmounts.totalFeePaidByTaker = erc20BalancesByOwner[takerAddress][this._feeTokenAddress].sub(newERC20BalancesByOwner[takerAddress][this._feeTokenAddress]); + actualTransferAmounts.totalFeePaidByTaker = erc20BalancesByOwner[takerAddress][this._feeTokenAddress].sub(newERC20BalancesByOwner[takerAddress][this._feeTokenAddress]); console.log("amountBoughtByLeftMaker"); - expect(expectedTransferAmounts.amountBoughtByLeftMaker).to.be.bignumber.equal(actualTransferAmounts.amountBoughtByLeftMaker); + // expect(expectedTransferAmounts.amountBoughtByLeftMaker).to.be.bignumber.equal(actualTransferAmounts.amountBoughtByLeftMaker); console.log("amountSoldByLeftMaker"); - expect(expectedTransferAmounts.amountSoldByLeftMaker).to.be.bignumber.equal(actualTransferAmounts.amountSoldByLeftMaker); + // expect(expectedTransferAmounts.amountSoldByLeftMaker).to.be.bignumber.equal(actualTransferAmounts.amountSoldByLeftMaker); console.log("amountBoughtByRightMaker"); - expect(expectedTransferAmounts.amountBoughtByRightMaker).to.be.bignumber.equal(actualTransferAmounts.amountBoughtByRightMaker); + // expect(expectedTransferAmounts.amountBoughtByRightMaker).to.be.bignumber.equal(actualTransferAmounts.amountBoughtByRightMaker); console.log("amountSoldByRightMaker"); - expect(expectedTransferAmounts.amountSoldByRightMaker).to.be.bignumber.equal(actualTransferAmounts.amountSoldByRightMaker); + //expect(expectedTransferAmounts.amountSoldByRightMaker).to.be.bignumber.equal(actualTransferAmounts.amountSoldByRightMaker); console.log("amountReceivedByTaker"); - expect(expectedTransferAmounts.amountReceivedByTaker).to.be.bignumber.equal(actualTransferAmounts.amountReceivedByTaker); + //expect(expectedTransferAmounts.amountReceivedByTaker).to.be.bignumber.equal(actualTransferAmounts.amountReceivedByTaker); console.log("feePaidByLeftMaker"); - expect(expectedTransferAmounts.feePaidByLeftMaker).to.be.bignumber.equal(actualTransferAmounts.feePaidByLeftMaker); + // expect(expectedTransferAmounts.feePaidByLeftMaker).to.be.bignumber.equal(actualTransferAmounts.feePaidByLeftMaker); console.log("feePaidByRightMaker"); - expect(expectedTransferAmounts.feePaidByRightMaker).to.be.bignumber.equal(actualTransferAmounts.feePaidByRightMaker); + //expect(expectedTransferAmounts.feePaidByRightMaker).to.be.bignumber.equal(actualTransferAmounts.feePaidByRightMaker); console.log("totalFeePaidByTaker"); - expect(expectedTransferAmounts.totalFeePaidByTaker).to.be.bignumber.equal(actualTransferAmounts.totalFeePaidByTaker); + // expect(expectedTransferAmounts.totalFeePaidByTaker).to.be.bignumber.equal(actualTransferAmounts.totalFeePaidByTaker); @@ -204,11 +287,12 @@ export class MatchOrderTester { // Fee Recipients feeReceivedLeft, feeReceivedRight, - };*/ + }; // This is a catch-all to ensure that no other balances changed console.log("Catch-all"); expect(didExpectedBalancesMatchRealBalances).to.be.true(); + */ return [newERC20BalancesByOwner, newERC721TokenIdsByOwner]; @@ -351,6 +435,13 @@ export class MatchOrderTester { _.remove(expectedNewERC721TokenIdsByOwner[makerAddressLeft][makerAssetAddressLeft], makerAssetIdLeft); // Right Maker expectedNewERC721TokenIdsByOwner[makerAddressRight][takerAssetAddressRight].push(takerAssetIdRight); + console.log("*** TRANSFERINF: " + JSON.stringify(makerAssetIdLeft)); + console.log("****"); + console.log(JSON.stringify(expectedNewERC721TokenIdsByOwner[makerAddressLeft][makerAssetAddressLeft])); + console.log("****"); + console.log(JSON.stringify(expectedNewERC721TokenIdsByOwner[makerAddressRight][takerAssetAddressRight])); + console.log("****"); + // Taker: Since there is only 1 asset transferred, the taker does not receive any of the left maker asset. } // Left Taker Asset (Right Maker Asset) -- cgit From f697814849142a8a98229e17433f942eef5c25d3 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 22 Aug 2018 14:05:44 -0700 Subject: First balance test with intentional values --- packages/contracts/test/exchange/match_orders.ts | 35 ++++-- .../contracts/test/utils/match_order_tester.ts | 119 ++++++++++++--------- 2 files changed, 95 insertions(+), 59 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index b2ffa31b2..3cffef7ad 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -21,10 +21,11 @@ import { ERC721Wrapper } from '../utils/erc721_wrapper'; import { ExchangeWrapper } from '../utils/exchange_wrapper'; import { MatchOrderTester } from '../utils/match_order_tester'; import { OrderFactory } from '../utils/order_factory'; -import { ERC20BalancesByOwner, ERC721TokenIdsByOwner, OrderInfo, OrderStatus } from '../utils/types'; +import { ERC20BalancesByOwner, ERC721TokenIdsByOwner, OrderInfo, TransferAmountsByMatchOrders as TransferAmounts, OrderStatus } from '../utils/types'; import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; chaiSetup.configure(); + const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); @@ -212,7 +213,7 @@ describe.only('matchOrders', () => { }); */ - it('Jacobs Example', async () => { + it.only('Jacobs Example', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ makerAddress: makerAddressLeft, @@ -228,6 +229,23 @@ describe.only('matchOrders', () => { takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 0), feeRecipientAddress: feeRecipientAddressRight, }); + // TODO: These values will change after implementation of rounding up has been merged + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 0), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 16), // 75% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(9), 0), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 17), // 50% + }; + const expectedEndStateLeft = OrderStatus.FILLABLE; + const expectedEndStateRight = OrderStatus.FULLY_FILLED; // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, @@ -235,15 +253,10 @@ describe.only('matchOrders', () => { takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts, + expectedEndStateLeft, + expectedEndStateRight ); - // // Verify left order was fully filled - const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - //expect(leftOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - console.log("*** LEFT ORDER INFO ***\n", JSON.stringify(leftOrderInfo), "\n***************"); - // Verify right order was fully filled - const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); - // expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - console.log("*** RIGHT ORDER INFO ***\n", JSON.stringify(rightOrderInfo), "\n***************"); }); @@ -825,6 +838,6 @@ describe.only('matchOrders', () => { // Verify right order was fully filled const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - }); + });*/ }); }); // tslint:disable-line:max-file-line-count diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index f9e45319a..7b071015f 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -8,7 +8,7 @@ import { chaiSetup } from './chai_setup'; import { ERC20Wrapper } from './erc20_wrapper'; import { ERC721Wrapper } from './erc721_wrapper'; import { ExchangeWrapper } from './exchange_wrapper'; -import { ERC20BalancesByOwner, ERC721TokenIdsByOwner, TransferAmountsByMatchOrders as TransferAmounts } from './types'; +import { ERC20BalancesByOwner, ERC721TokenIdsByOwner, TransferAmountsByMatchOrders as TransferAmounts , OrderStatus} from './types'; import { TransactionReceiptWithDecodedLogs } from '../../../../node_modules/ethereum-types'; chaiSetup.configure(); @@ -58,25 +58,30 @@ export class MatchOrderTester { return doesErc721TokenIdsMatch; } - /* - private static compareTokenIdLists(list1: BigNumber[], list2: BigNumber[]) { - // ERC721 Token Ids - list1 = _.(list1); - _.sortBy(tokenIds); - }); - }, + private async _verifyInitialOrderStates( + signedOrderLeft: SignedOrder, + signedOrderRight: SignedOrder, + initialTakerAssetFilledAmountLeft?: BigNumber, + initialTakerAssetFilledAmountRight?: BigNumber, + ): Promise<[BigNumber, BigNumber]> { + // Verify Left order preconditions + const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( + orderHashUtils.getOrderHashHex(signedOrderLeft), ); - const sortedNewERC721TokenIdsByOwner = _.mapValues(realERC721TokenIdsByOwner, tokenIdsByOwner => { - _.mapValues(tokenIdsByOwner, tokenIds => { - _.sortBy(tokenIds); - }); - }); - const doesErc721TokenIdsMatch = _.isEqual( - sortedExpectedNewERC721TokenIdsByOwner, - sortedNewERC721TokenIdsByOwner, + const expectedOrderFilledAmountLeft = initialTakerAssetFilledAmountLeft + ? initialTakerAssetFilledAmountLeft + : new BigNumber(0); + expect(expectedOrderFilledAmountLeft).to.be.bignumber.equal(orderTakerAssetFilledAmountLeft); + // Verify Right order preconditions + const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( + orderHashUtils.getOrderHashHex(signedOrderRight), ); - return doesErc721TokenIdsMatch; - }*/ + const expectedOrderFilledAmountRight = initialTakerAssetFilledAmountRight + ? initialTakerAssetFilledAmountRight + : new BigNumber(0); + expect(expectedOrderFilledAmountRight).to.be.bignumber.equal(orderTakerAssetFilledAmountRight); + return [orderTakerAssetFilledAmountLeft, orderTakerAssetFilledAmountRight]; + } /// @dev Constructs new MatchOrderTester. /// @param exchangeWrapper Used to call to the Exchange. @@ -110,30 +115,45 @@ export class MatchOrderTester { takerAddress: string, erc20BalancesByOwner: ERC20BalancesByOwner, erc721TokenIdsByOwner: ERC721TokenIdsByOwner, + expectedTransferAmounts: TransferAmounts, + leftOrderEndState: OrderStatus, + rightOrderEndState: OrderStatus, initialTakerAssetFilledAmountLeft?: BigNumber, initialTakerAssetFilledAmountRight?: BigNumber, ): Promise<[ERC20BalancesByOwner, ERC721TokenIdsByOwner]> { - // Verify Left order preconditions - const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrderLeft), - ); - const expectedOrderFilledAmountLeft = initialTakerAssetFilledAmountLeft - ? initialTakerAssetFilledAmountLeft - : new BigNumber(0); - expect(expectedOrderFilledAmountLeft).to.be.bignumber.equal(orderTakerAssetFilledAmountLeft); - // Verify Right order preconditions - const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrderRight), + // Verify initial order states + let orderTakerAssetFilledAmountLeft: BigNumber; + let orderTakerAssetFilledAmountRight: BigNumber; + [orderTakerAssetFilledAmountLeft, orderTakerAssetFilledAmountRight] = await this._verifyInitialOrderStates( + signedOrderLeft, + signedOrderRight, + initialTakerAssetFilledAmountLeft, + initialTakerAssetFilledAmountRight ); - const expectedOrderFilledAmountRight = initialTakerAssetFilledAmountRight - ? initialTakerAssetFilledAmountRight - : new BigNumber(0); - expect(expectedOrderFilledAmountRight).to.be.bignumber.equal(orderTakerAssetFilledAmountRight); // Match left & right orders const transactionReceipt = await this._exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress); const newERC20BalancesByOwner = await this._erc20Wrapper.getBalancesAsync(); const newERC721TokenIdsByOwner = await this._erc721Wrapper.getBalancesAsync(); + // Calculate expected fees + + + // Verify logs + + // Verify balances + + + + console.log(JSON.stringify(transactionReceipt, null, 4)); + + // Verify logs (and return values) + + // Verify exchange state + + // Verify balances + + // Calculate expected balance changes + /* const expectedTransferAmounts = await this._calculateExpectedTransferAmountsAsync( signedOrderLeft, signedOrderRight, @@ -141,7 +161,7 @@ export class MatchOrderTester { orderTakerAssetFilledAmountRight, transactionReceipt, takerAddress - ); + );*/ let expectedERC20BalancesByOwner: ERC20BalancesByOwner; let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner; [expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances( @@ -169,6 +189,7 @@ export class MatchOrderTester { const makerAssetAddressRight = makerERC20AssetDataRight.tokenAddress; console.log("Left Maker: Sell Amount"); + // describe('asdad', () => { if(makerAssetProxyIdLeft == AssetProxyId.ERC20) { expect(newERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]); } else if(makerAssetProxyIdLeft == AssetProxyId.ERC721) { @@ -177,6 +198,8 @@ export class MatchOrderTester { throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`); } + // }); + console.log("Left Maker: Buy Amount"); if(makerAssetProxyIdRight == AssetProxyId.ERC20) { expect(newERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]); @@ -292,11 +315,12 @@ export class MatchOrderTester { // This is a catch-all to ensure that no other balances changed console.log("Catch-all"); expect(didExpectedBalancesMatchRealBalances).to.be.true(); - */ - + +*/ return [newERC20BalancesByOwner, newERC721TokenIdsByOwner]; } + /// @dev Calculates expected transfer amounts between order makers, fee recipients, and /// the taker when two orders are matched. /// @param signedOrderLeft First matched order. @@ -312,7 +336,7 @@ export class MatchOrderTester { transactionReceipt: TransactionReceiptWithDecodedLogs, takerAddress: string ): Promise { - // Parse logs + // Parse logs expect(transactionReceipt.logs.length).to.be.equal(2); // First log is for left fill const leftLog = ((transactionReceipt.logs[0] as any) as {args: { makerAddress: string, takerAddress: string, makerAssetFilledAmount: string, takerAssetFilledAmount: string, makerFeePaid: string, takerFeePaid: string}}); @@ -344,7 +368,6 @@ export class MatchOrderTester { const feePaidByRightMaker = signedOrderRight.makerFee .times(amountSoldByRightMaker) .dividedToIntegerBy(signedOrderRight.makerAssetAmount); - const feePaidByTakerLeft = signedOrderLeft.takerFee .times(amountBoughtByLeftMaker) .dividedToIntegerBy(signedOrderLeft.takerAssetAmount); @@ -361,21 +384,21 @@ export class MatchOrderTester { // Left Maker amountBoughtByLeftMaker, amountSoldByLeftMaker, - amountReceivedByLeftMaker, + // amountReceivedByLeftMaker, feePaidByLeftMaker, // Right Maker amountBoughtByRightMaker, amountSoldByRightMaker, - amountReceivedByRightMaker, + // amountReceivedByRightMaker, feePaidByRightMaker, // Taker amountReceivedByTaker, feePaidByTakerLeft, feePaidByTakerRight, - totalFeePaidByTaker, + // totalFeePaidByTaker, // Fee Recipients - feeReceivedLeft, - feeReceivedRight, + // feeReceivedLeft, + // feeReceivedRight, }; return expectedTransferAmounts; } @@ -418,7 +441,7 @@ export class MatchOrderTester { expectedNewERC20BalancesByOwner[makerAddressRight][ takerAssetAddressRight ] = expectedNewERC20BalancesByOwner[makerAddressRight][takerAssetAddressRight].add( - expectedTransferAmounts.amountReceivedByRightMaker, + expectedTransferAmounts.amountBoughtByRightMaker, ); // Taker expectedNewERC20BalancesByOwner[takerAddress][makerAssetAddressLeft] = expectedNewERC20BalancesByOwner[ @@ -455,7 +478,7 @@ export class MatchOrderTester { // Left Maker expectedNewERC20BalancesByOwner[makerAddressLeft][takerAssetAddressLeft] = expectedNewERC20BalancesByOwner[ makerAddressLeft - ][takerAssetAddressLeft].add(expectedTransferAmounts.amountReceivedByLeftMaker); + ][takerAssetAddressLeft].add(expectedTransferAmounts.amountBoughtByLeftMaker); // Right Maker expectedNewERC20BalancesByOwner[makerAddressRight][ makerAssetAddressRight @@ -485,18 +508,18 @@ export class MatchOrderTester { // Taker Fees expectedNewERC20BalancesByOwner[takerAddress][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[ takerAddress - ][this._feeTokenAddress].minus(expectedTransferAmounts.totalFeePaidByTaker); + ][this._feeTokenAddress].minus(expectedTransferAmounts.feePaidByTakerLeft.add(expectedTransferAmounts.feePaidByTakerRight)); // Left Fee Recipient Fees expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][ this._feeTokenAddress ] = expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][this._feeTokenAddress].add( - expectedTransferAmounts.feeReceivedLeft, + expectedTransferAmounts.feePaidByLeftMaker.add(expectedTransferAmounts.feePaidByTakerLeft), ); // Right Fee Recipient Fees expectedNewERC20BalancesByOwner[feeRecipientAddressRight][ this._feeTokenAddress ] = expectedNewERC20BalancesByOwner[feeRecipientAddressRight][this._feeTokenAddress].add( - expectedTransferAmounts.feeReceivedRight, + expectedTransferAmounts.feePaidByRightMaker.add(expectedTransferAmounts.feePaidByTakerRight), ); return [expectedNewERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner]; -- cgit From 8c706ac6392c79ef46da37dd4b76f58f7d3f57e4 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 22 Aug 2018 15:22:27 -0700 Subject: Verify logs --- .../contracts/test/utils/match_order_tester.ts | 116 ++++++++------------- 1 file changed, 44 insertions(+), 72 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index 7b071015f..15915e511 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -134,10 +134,15 @@ export class MatchOrderTester { const transactionReceipt = await this._exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress); const newERC20BalancesByOwner = await this._erc20Wrapper.getBalancesAsync(); const newERC721TokenIdsByOwner = await this._erc721Wrapper.getBalancesAsync(); - // Calculate expected fees - - - // Verify logs + // Verify logs & return values + this._verifyLogsAsync( + signedOrderLeft, + signedOrderRight, + transactionReceipt, + takerAddress, + expectedTransferAmounts + ); + // Verify exchange state // Verify balances @@ -328,78 +333,45 @@ export class MatchOrderTester { /// @param orderTakerAssetFilledAmountLeft How much left order has been filled, prior to matching orders. /// @param orderTakerAssetFilledAmountRight How much the right order has been filled, prior to matching orders. /// @return TransferAmounts A struct containing the expected transfer amounts. - private async _calculateExpectedTransferAmountsAsync( + private async _verifyLogsAsync( signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder, - orderTakerAssetFilledAmountLeft: BigNumber, - orderTakerAssetFilledAmountRight: BigNumber, transactionReceipt: TransactionReceiptWithDecodedLogs, - takerAddress: string - ): Promise { + takerAddress: string, + expectedTransferAmounts: TransferAmounts + ) { // Parse logs - expect(transactionReceipt.logs.length).to.be.equal(2); - // First log is for left fill - const leftLog = ((transactionReceipt.logs[0] as any) as {args: { makerAddress: string, takerAddress: string, makerAssetFilledAmount: string, takerAssetFilledAmount: string, makerFeePaid: string, takerFeePaid: string}}); - expect(leftLog.args.makerAddress).to.be.equal(signedOrderLeft.makerAddress); - expect(leftLog.args.takerAddress).to.be.equal(takerAddress); - const amountBoughtByLeftMaker = new BigNumber(leftLog.args.takerAssetFilledAmount); - const amountSoldByLeftMaker = new BigNumber(leftLog.args.makerAssetFilledAmount); - // Second log is for right fill - const rightLog = ((transactionReceipt.logs[1] as any) as {args: { makerAddress: string, takerAddress: string, makerAssetFilledAmount: string, takerAssetFilledAmount: string, makerFeePaid: string, takerFeePaid: string}}); - expect(rightLog.args.makerAddress).to.be.equal(signedOrderRight.makerAddress); - expect(rightLog.args.takerAddress).to.be.equal(takerAddress); - const amountBoughtByRightMaker = new BigNumber(rightLog.args.takerAssetFilledAmount); - const amountSoldByRightMaker = new BigNumber(rightLog.args.makerAssetFilledAmount); - // Determine amount received by taker - const amountReceivedByTaker = amountSoldByLeftMaker.sub(amountBoughtByRightMaker); - - const amountReceivedByLeftMaker = amountBoughtByLeftMaker; - const amountReceivedByRightMaker = amountBoughtByRightMaker; - - console.log("Amount bought by left maker = ", JSON.stringify(amountBoughtByLeftMaker)); - console.log("Amount sold by left maker = ", JSON.stringify(amountSoldByLeftMaker)); - console.log("Amount bought by right maker = ", JSON.stringify(amountBoughtByRightMaker)); - console.log("Amount sold by right maker = ", JSON.stringify(amountSoldByRightMaker)); - console.log("Amount received by taker = ", JSON.stringify(amountReceivedByTaker)); - //const amountReceivedByLeftMaker = amountSoldByRightMaker; - const feePaidByLeftMaker = signedOrderLeft.makerFee - .times(amountSoldByLeftMaker) - .dividedToIntegerBy(signedOrderLeft.makerAssetAmount); - const feePaidByRightMaker = signedOrderRight.makerFee - .times(amountSoldByRightMaker) - .dividedToIntegerBy(signedOrderRight.makerAssetAmount); - const feePaidByTakerLeft = signedOrderLeft.takerFee - .times(amountBoughtByLeftMaker) - .dividedToIntegerBy(signedOrderLeft.takerAssetAmount); - - - const feePaidByTakerRight = signedOrderRight.takerFee - .times(amountBoughtByRightMaker) - .dividedToIntegerBy(signedOrderRight.takerAssetAmount); - const totalFeePaidByTaker = feePaidByTakerLeft.add(feePaidByTakerRight); - const feeReceivedLeft = feePaidByLeftMaker.add(feePaidByTakerLeft); - const feeReceivedRight = feePaidByRightMaker.add(feePaidByTakerRight); - // Return values - const expectedTransferAmounts = { - // Left Maker - amountBoughtByLeftMaker, - amountSoldByLeftMaker, - // amountReceivedByLeftMaker, - feePaidByLeftMaker, - // Right Maker - amountBoughtByRightMaker, - amountSoldByRightMaker, - // amountReceivedByRightMaker, - feePaidByRightMaker, - // Taker - amountReceivedByTaker, - feePaidByTakerLeft, - feePaidByTakerRight, - // totalFeePaidByTaker, - // Fee Recipients - // feeReceivedLeft, - // feeReceivedRight, - }; + expect(transactionReceipt.logs.length, "Checking number of logs").to.be.equal(2); + // First log is for left fill + const leftLog = ((transactionReceipt.logs[0] as any) as {args: { makerAddress: string, takerAddress: string, makerAssetFilledAmount: string, takerAssetFilledAmount: string, makerFeePaid: string, takerFeePaid: string}}); + expect(leftLog.args.makerAddress, "Checking logged maker address of left order").to.be.equal(signedOrderLeft.makerAddress); + expect(leftLog.args.takerAddress, "Checking logged taker address of right order").to.be.equal(takerAddress); + const amountBoughtByLeftMaker = new BigNumber(leftLog.args.takerAssetFilledAmount); + const amountSoldByLeftMaker = new BigNumber(leftLog.args.makerAssetFilledAmount); + const feePaidByLeftMaker = new BigNumber(leftLog.args.makerFeePaid); + const feePaidByTakerLeft = new BigNumber(leftLog.args.takerFeePaid); + // Second log is for right fill + const rightLog = ((transactionReceipt.logs[1] as any) as {args: { makerAddress: string, takerAddress: string, makerAssetFilledAmount: string, takerAssetFilledAmount: string, makerFeePaid: string, takerFeePaid: string}}); + expect(rightLog.args.makerAddress, "Checking logged maker address of right order").to.be.equal(signedOrderRight.makerAddress); + expect(rightLog.args.takerAddress, "Checking loggerd taker address of right order").to.be.equal(takerAddress); + const amountBoughtByRightMaker = new BigNumber(rightLog.args.takerAssetFilledAmount); + const amountSoldByRightMaker = new BigNumber(rightLog.args.makerAssetFilledAmount); + const feePaidByRightMaker = new BigNumber(rightLog.args.makerFeePaid); + const feePaidByTakerRight = new BigNumber(rightLog.args.takerFeePaid); + // Derive amount received by taker + const amountReceivedByTaker = amountSoldByLeftMaker.sub(amountBoughtByRightMaker); + // Verify log values - left order + expect(expectedTransferAmounts.amountBoughtByLeftMaker, "Checking logged amount bought by left maker").to.be.bignumber.equal(amountBoughtByLeftMaker); + expect(expectedTransferAmounts.amountSoldByLeftMaker, "Checking logged amount sold by left maker").to.be.bignumber.equal(amountSoldByLeftMaker); + expect(expectedTransferAmounts.feePaidByLeftMaker, "Checking logged fee paid by left maker").to.be.bignumber.equal(feePaidByLeftMaker); + expect(expectedTransferAmounts.feePaidByTakerLeft, "Checking logged fee paid on left order by taker").to.be.bignumber.equal(feePaidByTakerLeft); + // Verify log values - right order + expect(expectedTransferAmounts.amountBoughtByRightMaker, "Checking logged amount bought by right maker").to.be.bignumber.equal(amountBoughtByRightMaker); + expect(expectedTransferAmounts.amountSoldByRightMaker, "Checking logged amount sold by right maker").to.be.bignumber.equal(amountSoldByRightMaker); + expect(expectedTransferAmounts.feePaidByRightMaker, "Checking logged fee paid by right maker").to.be.bignumber.equal(feePaidByRightMaker); + expect(expectedTransferAmounts.feePaidByTakerRight, "Checking logged fee paid on right order by taker").to.be.bignumber.equal(feePaidByTakerRight); + // Verify deriv ed amount received by taker + expect(expectedTransferAmounts.amountReceivedByTaker, "Checking logged amount received by taker").to.be.bignumber.equal(amountReceivedByTaker); return expectedTransferAmounts; } /// @dev Calculates the expected balances of order makers, fee recipients, and the taker, -- cgit From d2912561585a044eec3cf82b65e76760e7cb47c3 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 22 Aug 2018 16:33:44 -0700 Subject: Passes comprehensive test --- .../contracts/test/utils/match_order_tester.ts | 332 +++++++++------------ 1 file changed, 141 insertions(+), 191 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index 15915e511..c412595f5 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -8,7 +8,7 @@ import { chaiSetup } from './chai_setup'; import { ERC20Wrapper } from './erc20_wrapper'; import { ERC721Wrapper } from './erc721_wrapper'; import { ExchangeWrapper } from './exchange_wrapper'; -import { ERC20BalancesByOwner, ERC721TokenIdsByOwner, TransferAmountsByMatchOrders as TransferAmounts , OrderStatus} from './types'; +import { ERC20BalancesByOwner, ERC721TokenIdsByOwner, TransferAmountsByMatchOrders as TransferAmounts, OrderInfo, OrderStatus} from './types'; import { TransactionReceiptWithDecodedLogs } from '../../../../node_modules/ethereum-types'; chaiSetup.configure(); @@ -20,23 +20,109 @@ export class MatchOrderTester { private readonly _erc721Wrapper: ERC721Wrapper; private readonly _feeTokenAddress: string; + private async _verifyBalancesAsync( + signedOrderLeft: SignedOrder, + signedOrderRight: SignedOrder, + initialERC20BalancesByOwner: ERC20BalancesByOwner, + initialERC721TokenIdsByOwner: ERC721TokenIdsByOwner, + finalERC20BalancesByOwner: ERC20BalancesByOwner, + finalERC721TokenIdsByOwner: ERC721TokenIdsByOwner, + expectedTransferAmounts: TransferAmounts, + takerAddress: string + ) + { + let expectedERC20BalancesByOwner: ERC20BalancesByOwner; + let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner; + [expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances( + signedOrderLeft, + signedOrderRight, + takerAddress, + initialERC20BalancesByOwner, + initialERC721TokenIdsByOwner, + expectedTransferAmounts, + ); + // Verify balances of makers, taker, and fee recipients + await this._verifyMakerTakerAndFeeRecipientBalancesAsync( + signedOrderLeft, + signedOrderRight, + expectedERC20BalancesByOwner, + finalERC20BalancesByOwner, + expectedERC721TokenIdsByOwner, + finalERC721TokenIdsByOwner, + takerAddress + ); + // Verify balances for all known accounts + await this._verifyAllKnownBalancesAsync( + expectedERC20BalancesByOwner, + finalERC20BalancesByOwner, + expectedERC721TokenIdsByOwner, + finalERC721TokenIdsByOwner, + ); + } + + private async _verifyMakerTakerAndFeeRecipientBalancesAsync( + signedOrderLeft: SignedOrder, + signedOrderRight: SignedOrder, + expectedERC20BalancesByOwner: ERC20BalancesByOwner, + realERC20BalancesByOwner: ERC20BalancesByOwner, + expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner, + realERC721TokenIdsByOwner: ERC721TokenIdsByOwner, + takerAddress: string + ) { + // Individual balance comparisons + const makerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.makerAssetData); + const makerERC20AssetDataLeft = makerAssetProxyIdLeft == AssetProxyId.ERC20 ? assetDataUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData) : assetDataUtils.decodeERC721AssetData(signedOrderLeft.makerAssetData); + const makerAssetAddressLeft = makerERC20AssetDataLeft.tokenAddress; + const makerAssetProxyIdRight = assetDataUtils.decodeAssetProxyId(signedOrderRight.makerAssetData); + const makerERC20AssetDataRight = makerAssetProxyIdRight == AssetProxyId.ERC20 ? assetDataUtils.decodeERC20AssetData(signedOrderRight.makerAssetData) : assetDataUtils.decodeERC721AssetData(signedOrderRight.makerAssetData); + const makerAssetAddressRight = makerERC20AssetDataRight.tokenAddress; + // describe('asdad', () => { + if(makerAssetProxyIdLeft == AssetProxyId.ERC20) { + expect(realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft], "Checking left maker egress ERC20 account balance").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]); + expect(realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft], "Checking right maker ingress ERC20 account balance").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft]); + expect(realERC20BalancesByOwner[takerAddress][makerAssetAddressLeft], "Checking taker ingress ERC20 account balance").to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][makerAssetAddressLeft]); + } else if(makerAssetProxyIdLeft == AssetProxyId.ERC721) { + expect(realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort(), "Checking left maker egress ERC721 account holdings").to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort()); + expect(realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort(), "Checking right maker ERC721 account holdings").to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort()); + expect(realERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort(), "Checking taker ingress ERC721 account holdings").to.be.deep.equal(expectedERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort()); + } else { + throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`); + } + if(makerAssetProxyIdRight == AssetProxyId.ERC20) { + expect(realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight], "Checking left maker ingress ERC20 account balance").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]); + expect(realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], "Checking right maker egress ERC20 account balance").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]); + } else if(makerAssetProxyIdRight == AssetProxyId.ERC721) { + expect(realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort(), "Checking left maker ingress ERC721 account holdings").to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort()); + expect(realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], "Checking right maker agress ERC721 account holdings").to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]); + } else { + throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdRight}`); + } + // Paid fees + expect(realERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress], "Checking left maker egress ERC20 account fees").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]); + expect(realERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress], "Checking right maker egress ERC20 account fees").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]); + expect(realERC20BalancesByOwner[takerAddress][this._feeTokenAddress], "Checking taker egress ERC20 account fees").to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][this._feeTokenAddress]); + // Received fees + expect(realERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress], "Checking left fee recipient ingress ERC20 account fees").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress]); + expect(realERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress], "Checking right fee receipient ingress ERC20 account fees").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress]); + } + /// @dev Compares a pair of ERC20 balances and a pair of ERC721 token owners. /// @param expectedNewERC20BalancesByOwner Expected ERC20 balances. /// @param realERC20BalancesByOwner Actual ERC20 balances. /// @param expectedNewERC721TokenIdsByOwner Expected ERC721 token owners. /// @param realERC721TokenIdsByOwner Actual ERC20 token owners. /// @return True only if ERC20 balances match and ERC721 token owners match. - private static _compareExpectedAndRealBalances( + private async _verifyAllKnownBalancesAsync( expectedNewERC20BalancesByOwner: ERC20BalancesByOwner, realERC20BalancesByOwner: ERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner: ERC721TokenIdsByOwner, realERC721TokenIdsByOwner: ERC721TokenIdsByOwner, - ): boolean { + ) { + // ERC20 Balances - const doesErc20BalancesMatch = _.isEqual(expectedNewERC20BalancesByOwner, realERC20BalancesByOwner); - if (!doesErc20BalancesMatch) { - return false; - } + const erc20BalancesMatch = _.isEqual(expectedNewERC20BalancesByOwner, realERC20BalancesByOwner); + expect(erc20BalancesMatch, "Checking all known ERC20 account balances").to.be.true(); + // ERC721 Token Ids const sortedExpectedNewERC721TokenIdsByOwner = _.mapValues( expectedNewERC721TokenIdsByOwner, @@ -51,11 +137,11 @@ export class MatchOrderTester { _.sortBy(tokenIds); }); }); - const doesErc721TokenIdsMatch = _.isEqual( + const erc721TokenIdsMatch = _.isEqual( sortedExpectedNewERC721TokenIdsByOwner, sortedNewERC721TokenIdsByOwner, ); - return doesErc721TokenIdsMatch; + expect(erc721TokenIdsMatch, "Checking all known ERC721 account balances").to.be.true(); } private async _verifyInitialOrderStates( @@ -134,8 +220,8 @@ export class MatchOrderTester { const transactionReceipt = await this._exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress); const newERC20BalancesByOwner = await this._erc20Wrapper.getBalancesAsync(); const newERC721TokenIdsByOwner = await this._erc721Wrapper.getBalancesAsync(); - // Verify logs & return values - this._verifyLogsAsync( + // Verify logs + await this._verifyLogsAsync( signedOrderLeft, signedOrderRight, transactionReceipt, @@ -143,189 +229,26 @@ export class MatchOrderTester { expectedTransferAmounts ); // Verify exchange state - - // Verify balances - - - - console.log(JSON.stringify(transactionReceipt, null, 4)); - - // Verify logs (and return values) - - // Verify exchange state - - // Verify balances - - - // Calculate expected balance changes - /* - const expectedTransferAmounts = await this._calculateExpectedTransferAmountsAsync( + await this._verifyExchangeStateAsync( signedOrderLeft, signedOrderRight, orderTakerAssetFilledAmountLeft, orderTakerAssetFilledAmountRight, - transactionReceipt, - takerAddress - );*/ - let expectedERC20BalancesByOwner: ERC20BalancesByOwner; - let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner; - [expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances( + expectedTransferAmounts + ); + // Verify balances of makers, taker, and fee recipients + await this._verifyBalancesAsync( signedOrderLeft, signedOrderRight, - takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, - expectedTransferAmounts, - ); - // Assert our expected balances are equal to the actual balances - const didExpectedBalancesMatchRealBalances = MatchOrderTester._compareExpectedAndRealBalances( - expectedERC20BalancesByOwner, newERC20BalancesByOwner, - expectedERC721TokenIdsByOwner, newERC721TokenIdsByOwner, + expectedTransferAmounts, + takerAddress ); - - // Individual balance comparisons - const makerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.makerAssetData); - const makerERC20AssetDataLeft = makerAssetProxyIdLeft == AssetProxyId.ERC20 ? assetDataUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData) : assetDataUtils.decodeERC721AssetData(signedOrderLeft.makerAssetData); - const makerAssetAddressLeft = makerERC20AssetDataLeft.tokenAddress; - const makerAssetProxyIdRight = assetDataUtils.decodeAssetProxyId(signedOrderRight.makerAssetData); - const makerERC20AssetDataRight = makerAssetProxyIdRight == AssetProxyId.ERC20 ? assetDataUtils.decodeERC20AssetData(signedOrderRight.makerAssetData) : assetDataUtils.decodeERC721AssetData(signedOrderRight.makerAssetData); - const makerAssetAddressRight = makerERC20AssetDataRight.tokenAddress; - - console.log("Left Maker: Sell Amount"); - // describe('asdad', () => { - if(makerAssetProxyIdLeft == AssetProxyId.ERC20) { - expect(newERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]); - } else if(makerAssetProxyIdLeft == AssetProxyId.ERC721) { - expect(newERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort()).to.be.deep.equal(newERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort()); - } else { - throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`); - } - - // }); - - console.log("Left Maker: Buy Amount"); - if(makerAssetProxyIdRight == AssetProxyId.ERC20) { - expect(newERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]); - } else if(makerAssetProxyIdRight == AssetProxyId.ERC721) { - expect(newERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort()).to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort()); - } else { - throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdRight}`); - } - - console.log("Left Maker: Fees"); - expect(newERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]); - - console.log("Right Maker: Sell Amount"); - if(makerAssetProxyIdRight == AssetProxyId.ERC20) { - expect(newERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]); - } else if(makerAssetProxyIdRight == AssetProxyId.ERC721) { - expect(newERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]).to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]); - } else { - throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdRight}`); - } - - console.log("Right Maker: Buy Amount"); - if(makerAssetProxyIdLeft == AssetProxyId.ERC20) { - expect(newERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft]).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft]); - } else if(makerAssetProxyIdLeft == AssetProxyId.ERC721) { - expect(newERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort()).to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort()); - } else { - throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`); - } - console.log("Right Maker: Fees"); - expect(newERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]); - console.log("Taker: Receive Amount"); - if(makerAssetProxyIdLeft == AssetProxyId.ERC20) { - expect(newERC20BalancesByOwner[takerAddress][makerAssetAddressLeft]).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][makerAssetAddressLeft]); - } else if(makerAssetProxyIdLeft == AssetProxyId.ERC721) { - expect(newERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort()).to.be.deep.equal(expectedERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort()); - } else { - throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`); - } - console.log("Taker: Fees"); - expect(newERC20BalancesByOwner[takerAddress][this._feeTokenAddress]).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][this._feeTokenAddress]); - console.log("Balance catch-all"); - expect(didExpectedBalancesMatchRealBalances).to.be.true(); - - /* - // Compute actual transfer amounts - let actualTransferAmounts = {}; - const makerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.makerAssetData); - if (makerAssetProxyIdLeft === AssetProxyId.ERC20) { - const erc20AssetData = assetDataUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData); - const makerAssetAddressLeft = erc20AssetData.tokenAddress; - actualTransferAmounts.amountSoldByLeftMaker = erc20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sub(newERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]); - actualTransferAmounts.amountBoughtByRightMaker = newERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sub(erc20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft]); - actualTransferAmounts.amountReceivedByRightMaker = actualTransferAmounts.amountBoughtByRightMaker; - actualTransferAmounts.amountReceivedByTaker = newERC20BalancesByOwner[takerAddress][makerAssetAddressLeft].sub(erc20BalancesByOwner[takerAddress][makerAssetAddressLeft]); - } else if(makerAssetProxyIdLeft === AssetProxyId.ERC721) { - } - const makerAssetProxyIdRight = assetDataUtils.decodeAssetProxyId(signedOrderRight.makerAssetData); - if (makerAssetProxyIdRight === AssetProxyId.ERC20) { - const erc20AssetData = assetDataUtils.decodeERC20AssetData(signedOrderRight.makerAssetData); - const makerAssetAddressRight = erc20AssetData.tokenAddress; - actualTransferAmounts.amountSoldByRightMaker = erc20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight].sub(newERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]); - actualTransferAmounts.amountBoughtByLeftMaker = newERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sub(erc20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]); - actualTransferAmounts.amountReceivedByLeftMaker = actualTransferAmounts.amountBoughtByLeftMaker; - } else if(makerAssetProxyIdRight === AssetProxyId.ERC721) { - } - // Fees - actualTransferAmounts.feePaidByLeftMaker = erc20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress].sub(newERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]); - actualTransferAmounts.feePaidByRightMaker = erc20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress].sub(newERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]); - actualTransferAmounts.totalFeePaidByTaker = erc20BalancesByOwner[takerAddress][this._feeTokenAddress].sub(newERC20BalancesByOwner[takerAddress][this._feeTokenAddress]); - - console.log("amountBoughtByLeftMaker"); - // expect(expectedTransferAmounts.amountBoughtByLeftMaker).to.be.bignumber.equal(actualTransferAmounts.amountBoughtByLeftMaker); - console.log("amountSoldByLeftMaker"); - // expect(expectedTransferAmounts.amountSoldByLeftMaker).to.be.bignumber.equal(actualTransferAmounts.amountSoldByLeftMaker); - console.log("amountBoughtByRightMaker"); - // expect(expectedTransferAmounts.amountBoughtByRightMaker).to.be.bignumber.equal(actualTransferAmounts.amountBoughtByRightMaker); - console.log("amountSoldByRightMaker"); - //expect(expectedTransferAmounts.amountSoldByRightMaker).to.be.bignumber.equal(actualTransferAmounts.amountSoldByRightMaker); - console.log("amountReceivedByTaker"); - //expect(expectedTransferAmounts.amountReceivedByTaker).to.be.bignumber.equal(actualTransferAmounts.amountReceivedByTaker); - console.log("feePaidByLeftMaker"); - // expect(expectedTransferAmounts.feePaidByLeftMaker).to.be.bignumber.equal(actualTransferAmounts.feePaidByLeftMaker); - console.log("feePaidByRightMaker"); - //expect(expectedTransferAmounts.feePaidByRightMaker).to.be.bignumber.equal(actualTransferAmounts.feePaidByRightMaker); - console.log("totalFeePaidByTaker"); - // expect(expectedTransferAmounts.totalFeePaidByTaker).to.be.bignumber.equal(actualTransferAmounts.totalFeePaidByTaker); - - - -/* - const actualTransferAmounts = { - // Left Maker - amountBoughtByLeftMaker, - amountSoldByLeftMaker, - amountReceivedByLeftMaker, - feePaidByLeftMaker, - // Right Maker - amountBoughtByRightMaker, - amountSoldByRightMaker, - amountReceivedByRightMaker, - feePaidByRightMaker, - // Taker - amountReceivedByTaker, - feePaidByTakerLeft, - feePaidByTakerRight, - totalFeePaidByTaker, - // Fee Recipients - feeReceivedLeft, - feeReceivedRight, - }; - - // This is a catch-all to ensure that no other balances changed - console.log("Catch-all"); - expect(didExpectedBalancesMatchRealBalances).to.be.true(); - - -*/ return [newERC20BalancesByOwner, newERC721TokenIdsByOwner]; } - /// @dev Calculates expected transfer amounts between order makers, fee recipients, and /// the taker when two orders are matched. /// @param signedOrderLeft First matched order. @@ -370,13 +293,47 @@ export class MatchOrderTester { expect(expectedTransferAmounts.amountSoldByRightMaker, "Checking logged amount sold by right maker").to.be.bignumber.equal(amountSoldByRightMaker); expect(expectedTransferAmounts.feePaidByRightMaker, "Checking logged fee paid by right maker").to.be.bignumber.equal(feePaidByRightMaker); expect(expectedTransferAmounts.feePaidByTakerRight, "Checking logged fee paid on right order by taker").to.be.bignumber.equal(feePaidByTakerRight); - // Verify deriv ed amount received by taker + // Verify derived amount received by taker expect(expectedTransferAmounts.amountReceivedByTaker, "Checking logged amount received by taker").to.be.bignumber.equal(amountReceivedByTaker); - return expectedTransferAmounts; + } + /// @dev Calculates expected transfer amounts between order makers, fee recipients, and + /// the taker when two orders are matched. + /// @param signedOrderLeft First matched order. + /// @param signedOrderRight Second matched order. + /// @param orderTakerAssetFilledAmountLeft How much left order has been filled, prior to matching orders. + /// @param orderTakerAssetFilledAmountRight How much the right order has been filled, prior to matching orders. + /// @return TransferAmounts A struct containing the expected transfer amounts. + private async _verifyExchangeStateAsync( + signedOrderLeft: SignedOrder, + signedOrderRight: SignedOrder, + orderTakerAssetFilledAmountLeft: BigNumber, + orderTakerAssetFilledAmountRight: BigNumber, + expectedTransferAmounts: TransferAmounts + ) { + // Verify state for left order: amount bought by left maker + let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( + orderHashUtils.getOrderHashHex(signedOrderLeft), + ); + amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(orderTakerAssetFilledAmountLeft); + expect(expectedTransferAmounts.amountBoughtByLeftMaker, "Checking exchange state for left order").to.be.bignumber.equal(amountBoughtByLeftMaker); + // Verify state for right order: amount bought by right maker + let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( + orderHashUtils.getOrderHashHex(signedOrderRight), + ); + amountBoughtByRightMaker = amountBoughtByRightMaker.minus(orderTakerAssetFilledAmountRight); + expect(expectedTransferAmounts.amountBoughtByRightMaker, "Checking exchange state for right order").to.be.bignumber.equal(amountBoughtByRightMaker); + // Verify left order status + const leftOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderLeft); + const leftExpectedStatus = (expectedTransferAmounts.amountBoughtByLeftMaker.equals(signedOrderLeft.takerAssetAmount)) ? OrderStatus.FULLY_FILLED : OrderStatus.FILLABLE; + expect(leftOrderInfo.orderStatus as OrderStatus, "Checking exchange status for left order").to.be.equal(leftExpectedStatus); + // Verify right order status + const rightOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderRight); + const rightExpectedStatus = (expectedTransferAmounts.amountBoughtByRightMaker.equals(signedOrderRight.takerAssetAmount)) ? OrderStatus.FULLY_FILLED : OrderStatus.FILLABLE; + expect(rightOrderInfo.orderStatus as OrderStatus, "Checking exchange status for right order").to.be.equal(rightExpectedStatus); } /// @dev Calculates the expected balances of order makers, fee recipients, and the taker, /// as a result of matching two orders. - /// @param signedOrderLeft First matched order. + /// @param signedOrderRight First matched order. /// @param signedOrderRight Second matched order. /// @param takerAddress Address of taker (the address who matched the two orders) /// @param erc20BalancesByOwner Current ERC20 balances. @@ -430,13 +387,6 @@ export class MatchOrderTester { _.remove(expectedNewERC721TokenIdsByOwner[makerAddressLeft][makerAssetAddressLeft], makerAssetIdLeft); // Right Maker expectedNewERC721TokenIdsByOwner[makerAddressRight][takerAssetAddressRight].push(takerAssetIdRight); - console.log("*** TRANSFERINF: " + JSON.stringify(makerAssetIdLeft)); - console.log("****"); - console.log(JSON.stringify(expectedNewERC721TokenIdsByOwner[makerAddressLeft][makerAssetAddressLeft])); - console.log("****"); - console.log(JSON.stringify(expectedNewERC721TokenIdsByOwner[makerAddressRight][takerAssetAddressRight])); - console.log("****"); - // Taker: Since there is only 1 asset transferred, the taker does not receive any of the left maker asset. } // Left Taker Asset (Right Maker Asset) -- cgit From 5a1dce15be60fa88d6e317cd6dfa7b993c90c257 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 22 Aug 2018 20:17:23 -0700 Subject: Updated all existing match order tests to use new format --- packages/contracts/test/exchange/match_orders.ts | 409 +++++++++++++++------ .../contracts/test/utils/match_order_tester.ts | 20 +- 2 files changed, 301 insertions(+), 128 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index 3cffef7ad..eb2644ba6 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -21,7 +21,13 @@ import { ERC721Wrapper } from '../utils/erc721_wrapper'; import { ExchangeWrapper } from '../utils/exchange_wrapper'; import { MatchOrderTester } from '../utils/match_order_tester'; import { OrderFactory } from '../utils/order_factory'; -import { ERC20BalancesByOwner, ERC721TokenIdsByOwner, OrderInfo, TransferAmountsByMatchOrders as TransferAmounts, OrderStatus } from '../utils/types'; +import { + ERC20BalancesByOwner, + ERC721TokenIdsByOwner, + OrderInfo, + TransferAmountsByMatchOrders as TransferAmounts, + OrderStatus, +} from '../utils/types'; import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; chaiSetup.configure(); @@ -177,7 +183,6 @@ describe.only('matchOrders', () => { erc20BalancesByOwner = await erc20Wrapper.getBalancesAsync(); erc721TokenIdsByOwner = await erc721Wrapper.getBalancesAsync(); }); - /* it.only('should transfer the correct amounts when orders completely fill each other', async () => { @@ -213,7 +218,7 @@ describe.only('matchOrders', () => { }); */ - it.only('Jacobs Example', async () => { + it('Jacobs Example', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ makerAddress: makerAddressLeft, @@ -244,8 +249,6 @@ describe.only('matchOrders', () => { feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), // 100% feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 17), // 50% }; - const expectedEndStateLeft = OrderStatus.FILLABLE; - const expectedEndStateRight = OrderStatus.FULLY_FILLED; // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, @@ -254,44 +257,8 @@ describe.only('matchOrders', () => { erc20BalancesByOwner, erc721TokenIdsByOwner, expectedTransferAmounts, - expectedEndStateLeft, - expectedEndStateRight ); }); - - - /* - it.only('Jacobs Example', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAddress: makerAddressLeft, - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), - feeRecipientAddress: feeRecipientAddressLeft, - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAddress: makerAddressRight, - makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), - takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), - feeRecipientAddress: feeRecipientAddressRight, - }); - // Match signedOrderLeft with signedOrderRight - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - ); - // // Verify left order was fully filled - // const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - // expect(leftOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - // Verify right order was fully filled - // const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); - // expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - });*/ const reentrancyTest = (functionNames: string[]) => { _.forEach(functionNames, async (functionName: string, functionId: number) => { @@ -333,19 +300,28 @@ describe.only('matchOrders', () => { takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), }); // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts, ); - // Verify left order was fully filled - const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - expect(leftOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - // Verify right order was fully filled - const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); - expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); }); it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => { @@ -358,32 +334,29 @@ describe.only('matchOrders', () => { makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), }); - // Store original taker balance - const takerInitialBalances = _.cloneDeep(erc20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress]); // Match signedOrderLeft with signedOrderRight - let newERC20BalancesByOwner: ERC20BalancesByOwner; - let newERC721TokenIdsByOwner: ERC721TokenIdsByOwner; - // prettier-ignore - [ - newERC20BalancesByOwner, - // tslint:disable-next-line:trailing-comma - newERC721TokenIdsByOwner - ] = await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + }; + // Match signedOrderLeft with signedOrderRight + await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, - ); - // Verify left order was fully filled - const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - expect(leftOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - // Verify right order was fully filled - const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); - expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - // Verify taker did not take a profit - expect(takerInitialBalances).to.be.deep.equal( - newERC20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress], + expectedTransferAmounts, ); }); @@ -397,20 +370,30 @@ describe.only('matchOrders', () => { makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), }); - // Match orders + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 16), // 50% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 40% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 16), // 100% + }; + // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts, ); - // Verify left order was fully filled - const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - expect(leftOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - // Verify right order was partially filled - const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); - expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FILLABLE); }); it('should transfer the correct amounts when right order is completely filled and left order is partially filled', async () => { @@ -423,20 +406,30 @@ describe.only('matchOrders', () => { makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), }); - // Match orders + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 16), // 10% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 50% + }; + // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts, ); - // Verify left order was partially filled - const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - expect(leftOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FILLABLE); - // Verify right order was fully filled - const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); - expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); }); it('should transfer the correct amounts when consecutive calls are used to completely fill the left order', async () => { @@ -452,6 +445,20 @@ describe.only('matchOrders', () => { // Match orders let newERC20BalancesByOwner: ERC20BalancesByOwner; let newERC721TokenIdsByOwner: ERC721TokenIdsByOwner; + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 16), // 10% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 50% + }; // prettier-ignore [ newERC20BalancesByOwner, @@ -463,13 +470,8 @@ describe.only('matchOrders', () => { takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts ); - // Verify left order was partially filled - const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - expect(leftOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FILLABLE); - // Verify right order was fully filled - const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); - expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); // Construct second right order // Note: This order needs makerAssetAmount=90/takerAssetAmount=[anything <= 45] to fully fill the right order. // However, we use 100/50 to ensure a partial fill as we want to go down the "left fill" @@ -478,24 +480,34 @@ describe.only('matchOrders', () => { makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), }); + // Match signedOrderLeft with signedOrderRight2 const leftTakerAssetFilledAmount = signedOrderRight.makerAssetAmount; const rightTakerAssetFilledAmount = new BigNumber(0); + const expectedTransferAmounts2 = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(45), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 16), // 90% (10% paid earlier) + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(45), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 16), // 90% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 16), // 90% (10% paid earlier) + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 16), // 90% + }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight2, takerAddress, newERC20BalancesByOwner, - erc721TokenIdsByOwner, + newERC721TokenIdsByOwner, + expectedTransferAmounts2, leftTakerAssetFilledAmount, rightTakerAssetFilledAmount, ); - // Verify left order was fully filled - const leftOrderInfo2: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - expect(leftOrderInfo2.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - // Verify second right order was partially filled - const rightOrderInfo2: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight2); - expect(rightOrderInfo2.orderStatus as OrderStatus).to.be.equal(OrderStatus.FILLABLE); }); it('should transfer the correct amounts when consecutive calls are used to completely fill the right order', async () => { @@ -512,6 +524,20 @@ describe.only('matchOrders', () => { // Match orders let newERC20BalancesByOwner: ERC20BalancesByOwner; let newERC721TokenIdsByOwner: ERC721TokenIdsByOwner; + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 16), // 4% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(6), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 16), // 4% + }; // prettier-ignore [ newERC20BalancesByOwner, @@ -523,13 +549,9 @@ describe.only('matchOrders', () => { takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts ); - // Verify left order was partially filled - const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - expect(leftOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - // Verify right order was fully filled - const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); - expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FILLABLE); + // Create second left order // Note: This order needs makerAssetAmount=96/takerAssetAmount=48 to fully fill the right order. // However, we use 100/50 to ensure a partial fill as we want to go down the "right fill" @@ -544,23 +566,58 @@ describe.only('matchOrders', () => { erc20BalancesByOwner[takerAddress][defaultERC20MakerAssetAddress], ); const rightTakerAssetFilledAmount = signedOrderLeft.makerAssetAmount.minus(takerAmountReceived); + const expectedTransferAmounts2 = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(96), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(48), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(96), 16), // 96% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(48), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(96), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(96), 16), // 96% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(96), 16), // 96% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(96), 16), // 96% + }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft2, signedOrderRight, takerAddress, newERC20BalancesByOwner, - erc721TokenIdsByOwner, + newERC721TokenIdsByOwner, + expectedTransferAmounts2, leftTakerAssetFilledAmount, rightTakerAssetFilledAmount, ); - // Verify second left order was partially filled - const leftOrderInfo2: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft2); - expect(leftOrderInfo2.orderStatus as OrderStatus).to.be.equal(OrderStatus.FILLABLE); - // Verify right order was fully filled - const rightOrderInfo2: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); - expect(rightOrderInfo2.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); }); + /* + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 16), // 10% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 16), // 50% + }; + await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + signedOrderLeft, + signedOrderRight, + takerAddress, + erc20BalancesByOwner, + erc721TokenIdsByOwner, + expectedTransferAmounts + ); + + */ it('should transfer the correct amounts if fee recipient is the same across both matched orders', async () => { const feeRecipientAddress = feeRecipientAddressLeft; const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ @@ -574,12 +631,27 @@ describe.only('matchOrders', () => { feeRecipientAddress, }); // Match orders + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts, ); }); @@ -595,12 +667,27 @@ describe.only('matchOrders', () => { }); // Match orders takerAddress = signedOrderLeft.makerAddress; + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts, ); }); @@ -616,12 +703,27 @@ describe.only('matchOrders', () => { }); // Match orders takerAddress = signedOrderRight.makerAddress; + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts, ); }); @@ -637,12 +739,27 @@ describe.only('matchOrders', () => { }); // Match orders takerAddress = feeRecipientAddressLeft; + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts, ); }); @@ -658,12 +775,27 @@ describe.only('matchOrders', () => { }); // Match orders takerAddress = feeRecipientAddressRight; + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts, ); }); @@ -678,12 +810,27 @@ describe.only('matchOrders', () => { takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), }); // Match orders + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts, ); }); @@ -796,22 +943,31 @@ describe.only('matchOrders', () => { takerAssetAmount: new BigNumber(1), }); // Match orders + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 50% + }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts, ); - // Verify left order was fully filled - const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - expect(leftOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - // Verify right order was fully filled - const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); - expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); }); - it('should transfer correct amounts when right order maker asset is an ERC721 token', async () => { + it.only('should transfer correct amounts when right order maker asset is an ERC721 token', async () => { // Create orders to match const erc721TokenToTransfer = erc721RightMakerAssetIds[0]; const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ @@ -822,22 +978,31 @@ describe.only('matchOrders', () => { const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ makerAssetData: assetDataUtils.encodeERC721AssetData(defaultERC721AssetAddress, erc721TokenToTransfer), makerAssetAmount: new BigNumber(1), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(8), 18), }); // Match orders + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(8), 18), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 50% + }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts, ); - // Verify left order was fully filled - const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - expect(leftOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - // Verify right order was fully filled - const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); - expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - });*/ + }); }); }); // tslint:disable-line:max-file-line-count diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index c412595f5..00816e7d4 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -202,8 +202,6 @@ export class MatchOrderTester { erc20BalancesByOwner: ERC20BalancesByOwner, erc721TokenIdsByOwner: ERC721TokenIdsByOwner, expectedTransferAmounts: TransferAmounts, - leftOrderEndState: OrderStatus, - rightOrderEndState: OrderStatus, initialTakerAssetFilledAmountLeft?: BigNumber, initialTakerAssetFilledAmountRight?: BigNumber, ): Promise<[ERC20BalancesByOwner, ERC721TokenIdsByOwner]> { @@ -234,7 +232,9 @@ export class MatchOrderTester { signedOrderRight, orderTakerAssetFilledAmountLeft, orderTakerAssetFilledAmountRight, - expectedTransferAmounts + expectedTransferAmounts, + initialTakerAssetFilledAmountLeft, + initialTakerAssetFilledAmountRight ); // Verify balances of makers, taker, and fee recipients await this._verifyBalancesAsync( @@ -308,7 +308,9 @@ export class MatchOrderTester { signedOrderRight: SignedOrder, orderTakerAssetFilledAmountLeft: BigNumber, orderTakerAssetFilledAmountRight: BigNumber, - expectedTransferAmounts: TransferAmounts + expectedTransferAmounts: TransferAmounts, + initialTakerAssetFilledAmountLeft?: BigNumber, + initialTakerAssetFilledAmountRight?: BigNumber ) { // Verify state for left order: amount bought by left maker let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( @@ -323,12 +325,18 @@ export class MatchOrderTester { amountBoughtByRightMaker = amountBoughtByRightMaker.minus(orderTakerAssetFilledAmountRight); expect(expectedTransferAmounts.amountBoughtByRightMaker, "Checking exchange state for right order").to.be.bignumber.equal(amountBoughtByRightMaker); // Verify left order status + const maxAmountBoughtByLeftMaker = initialTakerAssetFilledAmountLeft + ? signedOrderLeft.takerAssetAmount.sub(initialTakerAssetFilledAmountLeft) + : signedOrderLeft.takerAssetAmount; const leftOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - const leftExpectedStatus = (expectedTransferAmounts.amountBoughtByLeftMaker.equals(signedOrderLeft.takerAssetAmount)) ? OrderStatus.FULLY_FILLED : OrderStatus.FILLABLE; + const leftExpectedStatus = (expectedTransferAmounts.amountBoughtByLeftMaker.equals(maxAmountBoughtByLeftMaker)) ? OrderStatus.FULLY_FILLED : OrderStatus.FILLABLE; expect(leftOrderInfo.orderStatus as OrderStatus, "Checking exchange status for left order").to.be.equal(leftExpectedStatus); // Verify right order status + const maxAmountBoughtByRightMaker = initialTakerAssetFilledAmountRight + ? signedOrderRight.takerAssetAmount.sub(initialTakerAssetFilledAmountRight) + : signedOrderRight.takerAssetAmount; const rightOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderRight); - const rightExpectedStatus = (expectedTransferAmounts.amountBoughtByRightMaker.equals(signedOrderRight.takerAssetAmount)) ? OrderStatus.FULLY_FILLED : OrderStatus.FILLABLE; + const rightExpectedStatus = (expectedTransferAmounts.amountBoughtByRightMaker.equals(maxAmountBoughtByRightMaker)) ? OrderStatus.FULLY_FILLED : OrderStatus.FILLABLE; expect(rightOrderInfo.orderStatus as OrderStatus, "Checking exchange status for right order").to.be.equal(rightExpectedStatus); } /// @dev Calculates the expected balances of order makers, fee recipients, and the taker, -- cgit From 70863cca085c060d55932e5e173fc2023bda9df5 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 23 Aug 2018 10:31:06 -0700 Subject: Ran prettier and linter --- packages/contracts/test/exchange/match_orders.ts | 44 +- .../contracts/test/utils/match_order_tester.ts | 487 +++++++++++++-------- packages/contracts/test/utils/types.ts | 10 +- 3 files changed, 317 insertions(+), 224 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index eb2644ba6..7e9776d52 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -3,7 +3,6 @@ import { assetDataUtils } from '@0xproject/order-utils'; import { RevertReason } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; -import * as chai from 'chai'; import * as _ from 'lodash'; import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_erc20_token'; @@ -14,25 +13,15 @@ import { ExchangeContract } from '../../generated_contract_wrappers/exchange'; import { ReentrantERC20TokenContract } from '../../generated_contract_wrappers/reentrant_erc20_token'; import { artifacts } from '../utils/artifacts'; import { expectTransactionFailedAsync } from '../utils/assertions'; -import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; import { ERC721Wrapper } from '../utils/erc721_wrapper'; import { ExchangeWrapper } from '../utils/exchange_wrapper'; import { MatchOrderTester } from '../utils/match_order_tester'; import { OrderFactory } from '../utils/order_factory'; -import { - ERC20BalancesByOwner, - ERC721TokenIdsByOwner, - OrderInfo, - TransferAmountsByMatchOrders as TransferAmounts, - OrderStatus, -} from '../utils/types'; +import { ERC20BalancesByOwner, ERC721TokenIdsByOwner } from '../utils/types'; import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; -chaiSetup.configure(); - -const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); describe.only('matchOrders', () => { @@ -470,7 +459,7 @@ describe.only('matchOrders', () => { takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, - expectedTransferAmounts + expectedTransferAmounts, ); // Construct second right order // Note: This order needs makerAssetAmount=90/takerAssetAmount=[anything <= 45] to fully fill the right order. @@ -480,7 +469,6 @@ describe.only('matchOrders', () => { makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 18), }); - // Match signedOrderLeft with signedOrderRight2 const leftTakerAssetFilledAmount = signedOrderRight.makerAssetAmount; const rightTakerAssetFilledAmount = new BigNumber(0); @@ -549,7 +537,7 @@ describe.only('matchOrders', () => { takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, - expectedTransferAmounts + expectedTransferAmounts, ); // Create second left order @@ -592,32 +580,6 @@ describe.only('matchOrders', () => { ); }); - /* - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 18), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 18), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 16), // 10% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 18), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 18), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(), 16), // 50% - }; - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( - signedOrderLeft, - signedOrderRight, - takerAddress, - erc20BalancesByOwner, - erc721TokenIdsByOwner, - expectedTransferAmounts - ); - - */ it('should transfer the correct amounts if fee recipient is the same across both matched orders', async () => { const feeRecipientAddress = feeRecipientAddressLeft; const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index 00816e7d4..300073eb0 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -4,12 +4,19 @@ import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import * as _ from 'lodash'; +import { TransactionReceiptWithDecodedLogs } from '../../../../node_modules/ethereum-types'; + import { chaiSetup } from './chai_setup'; import { ERC20Wrapper } from './erc20_wrapper'; import { ERC721Wrapper } from './erc721_wrapper'; import { ExchangeWrapper } from './exchange_wrapper'; -import { ERC20BalancesByOwner, ERC721TokenIdsByOwner, TransferAmountsByMatchOrders as TransferAmounts, OrderInfo, OrderStatus} from './types'; -import { TransactionReceiptWithDecodedLogs } from '../../../../node_modules/ethereum-types'; +import { + ERC20BalancesByOwner, + ERC721TokenIdsByOwner, + OrderInfo, + OrderStatus, + TransferAmountsByMatchOrders as TransferAmounts, +} from './types'; chaiSetup.configure(); const expect = chai.expect; @@ -19,110 +26,117 @@ export class MatchOrderTester { private readonly _erc20Wrapper: ERC20Wrapper; private readonly _erc721Wrapper: ERC721Wrapper; private readonly _feeTokenAddress: string; - - private async _verifyBalancesAsync( + /// @dev Calculates expected transfer amounts between order makers, fee recipients, and + /// the taker when two orders are matched. + /// @param signedOrderLeft First matched order. + /// @param signedOrderRight Second matched order. + /// @param orderTakerAssetFilledAmountLeft How much left order has been filled, prior to matching orders. + /// @param orderTakerAssetFilledAmountRight How much the right order has been filled, prior to matching orders. + /// @return TransferAmounts A struct containing the expected transfer amounts. + private static async _verifyLogsAsync( signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder, - initialERC20BalancesByOwner: ERC20BalancesByOwner, - initialERC721TokenIdsByOwner: ERC721TokenIdsByOwner, - finalERC20BalancesByOwner: ERC20BalancesByOwner, - finalERC721TokenIdsByOwner: ERC721TokenIdsByOwner, + transactionReceipt: TransactionReceiptWithDecodedLogs, + takerAddress: string, expectedTransferAmounts: TransferAmounts, - takerAddress: string - ) - { - let expectedERC20BalancesByOwner: ERC20BalancesByOwner; - let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner; - [expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances( - signedOrderLeft, - signedOrderRight, - takerAddress, - initialERC20BalancesByOwner, - initialERC721TokenIdsByOwner, - expectedTransferAmounts, - ); - // Verify balances of makers, taker, and fee recipients - await this._verifyMakerTakerAndFeeRecipientBalancesAsync( - signedOrderLeft, - signedOrderRight, - expectedERC20BalancesByOwner, - finalERC20BalancesByOwner, - expectedERC721TokenIdsByOwner, - finalERC721TokenIdsByOwner, - takerAddress + ): Promise { + // Parse logs + expect(transactionReceipt.logs.length, 'Checking number of logs').to.be.equal(2); + // First log is for left fill + const leftLog = (transactionReceipt.logs[0] as any) as { + args: { + makerAddress: string; + takerAddress: string; + makerAssetFilledAmount: string; + takerAssetFilledAmount: string; + makerFeePaid: string; + takerFeePaid: string; + }; + }; + expect(leftLog.args.makerAddress, 'Checking logged maker address of left order').to.be.equal( + signedOrderLeft.makerAddress, ); - // Verify balances for all known accounts - await this._verifyAllKnownBalancesAsync( - expectedERC20BalancesByOwner, - finalERC20BalancesByOwner, - expectedERC721TokenIdsByOwner, - finalERC721TokenIdsByOwner, + expect(leftLog.args.takerAddress, 'Checking logged taker address of right order').to.be.equal(takerAddress); + const amountBoughtByLeftMaker = new BigNumber(leftLog.args.takerAssetFilledAmount); + const amountSoldByLeftMaker = new BigNumber(leftLog.args.makerAssetFilledAmount); + const feePaidByLeftMaker = new BigNumber(leftLog.args.makerFeePaid); + const feePaidByTakerLeft = new BigNumber(leftLog.args.takerFeePaid); + // Second log is for right fill + const rightLog = (transactionReceipt.logs[1] as any) as { + args: { + makerAddress: string; + takerAddress: string; + makerAssetFilledAmount: string; + takerAssetFilledAmount: string; + makerFeePaid: string; + takerFeePaid: string; + }; + }; + expect(rightLog.args.makerAddress, 'Checking logged maker address of right order').to.be.equal( + signedOrderRight.makerAddress, ); + expect(rightLog.args.takerAddress, 'Checking loggerd taker address of right order').to.be.equal(takerAddress); + const amountBoughtByRightMaker = new BigNumber(rightLog.args.takerAssetFilledAmount); + const amountSoldByRightMaker = new BigNumber(rightLog.args.makerAssetFilledAmount); + const feePaidByRightMaker = new BigNumber(rightLog.args.makerFeePaid); + const feePaidByTakerRight = new BigNumber(rightLog.args.takerFeePaid); + // Derive amount received by taker + const amountReceivedByTaker = amountSoldByLeftMaker.sub(amountBoughtByRightMaker); + // Verify log values - left order + expect( + expectedTransferAmounts.amountBoughtByLeftMaker, + 'Checking logged amount bought by left maker', + ).to.be.bignumber.equal(amountBoughtByLeftMaker); + expect( + expectedTransferAmounts.amountSoldByLeftMaker, + 'Checking logged amount sold by left maker', + ).to.be.bignumber.equal(amountSoldByLeftMaker); + expect( + expectedTransferAmounts.feePaidByLeftMaker, + 'Checking logged fee paid by left maker', + ).to.be.bignumber.equal(feePaidByLeftMaker); + expect( + expectedTransferAmounts.feePaidByTakerLeft, + 'Checking logged fee paid on left order by taker', + ).to.be.bignumber.equal(feePaidByTakerLeft); + // Verify log values - right order + expect( + expectedTransferAmounts.amountBoughtByRightMaker, + 'Checking logged amount bought by right maker', + ).to.be.bignumber.equal(amountBoughtByRightMaker); + expect( + expectedTransferAmounts.amountSoldByRightMaker, + 'Checking logged amount sold by right maker', + ).to.be.bignumber.equal(amountSoldByRightMaker); + expect( + expectedTransferAmounts.feePaidByRightMaker, + 'Checking logged fee paid by right maker', + ).to.be.bignumber.equal(feePaidByRightMaker); + expect( + expectedTransferAmounts.feePaidByTakerRight, + 'Checking logged fee paid on right order by taker', + ).to.be.bignumber.equal(feePaidByTakerRight); + // Verify derived amount received by taker + expect( + expectedTransferAmounts.amountReceivedByTaker, + 'Checking logged amount received by taker', + ).to.be.bignumber.equal(amountReceivedByTaker); } - - private async _verifyMakerTakerAndFeeRecipientBalancesAsync( - signedOrderLeft: SignedOrder, - signedOrderRight: SignedOrder, - expectedERC20BalancesByOwner: ERC20BalancesByOwner, - realERC20BalancesByOwner: ERC20BalancesByOwner, - expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner, - realERC721TokenIdsByOwner: ERC721TokenIdsByOwner, - takerAddress: string - ) { - // Individual balance comparisons - const makerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.makerAssetData); - const makerERC20AssetDataLeft = makerAssetProxyIdLeft == AssetProxyId.ERC20 ? assetDataUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData) : assetDataUtils.decodeERC721AssetData(signedOrderLeft.makerAssetData); - const makerAssetAddressLeft = makerERC20AssetDataLeft.tokenAddress; - const makerAssetProxyIdRight = assetDataUtils.decodeAssetProxyId(signedOrderRight.makerAssetData); - const makerERC20AssetDataRight = makerAssetProxyIdRight == AssetProxyId.ERC20 ? assetDataUtils.decodeERC20AssetData(signedOrderRight.makerAssetData) : assetDataUtils.decodeERC721AssetData(signedOrderRight.makerAssetData); - const makerAssetAddressRight = makerERC20AssetDataRight.tokenAddress; - // describe('asdad', () => { - if(makerAssetProxyIdLeft == AssetProxyId.ERC20) { - expect(realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft], "Checking left maker egress ERC20 account balance").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]); - expect(realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft], "Checking right maker ingress ERC20 account balance").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft]); - expect(realERC20BalancesByOwner[takerAddress][makerAssetAddressLeft], "Checking taker ingress ERC20 account balance").to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][makerAssetAddressLeft]); - } else if(makerAssetProxyIdLeft == AssetProxyId.ERC721) { - expect(realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort(), "Checking left maker egress ERC721 account holdings").to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort()); - expect(realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort(), "Checking right maker ERC721 account holdings").to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort()); - expect(realERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort(), "Checking taker ingress ERC721 account holdings").to.be.deep.equal(expectedERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort()); - } else { - throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`); - } - if(makerAssetProxyIdRight == AssetProxyId.ERC20) { - expect(realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight], "Checking left maker ingress ERC20 account balance").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]); - expect(realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], "Checking right maker egress ERC20 account balance").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]); - } else if(makerAssetProxyIdRight == AssetProxyId.ERC721) { - expect(realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort(), "Checking left maker ingress ERC721 account holdings").to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort()); - expect(realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], "Checking right maker agress ERC721 account holdings").to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]); - } else { - throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdRight}`); - } - // Paid fees - expect(realERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress], "Checking left maker egress ERC20 account fees").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]); - expect(realERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress], "Checking right maker egress ERC20 account fees").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]); - expect(realERC20BalancesByOwner[takerAddress][this._feeTokenAddress], "Checking taker egress ERC20 account fees").to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][this._feeTokenAddress]); - // Received fees - expect(realERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress], "Checking left fee recipient ingress ERC20 account fees").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress]); - expect(realERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress], "Checking right fee receipient ingress ERC20 account fees").to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress]); - } - /// @dev Compares a pair of ERC20 balances and a pair of ERC721 token owners. /// @param expectedNewERC20BalancesByOwner Expected ERC20 balances. /// @param realERC20BalancesByOwner Actual ERC20 balances. /// @param expectedNewERC721TokenIdsByOwner Expected ERC721 token owners. /// @param realERC721TokenIdsByOwner Actual ERC20 token owners. /// @return True only if ERC20 balances match and ERC721 token owners match. - private async _verifyAllKnownBalancesAsync( + private static async _verifyAllKnownBalancesAsync( expectedNewERC20BalancesByOwner: ERC20BalancesByOwner, realERC20BalancesByOwner: ERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner: ERC721TokenIdsByOwner, realERC721TokenIdsByOwner: ERC721TokenIdsByOwner, - ) { - + ): Promise { // ERC20 Balances - const erc20BalancesMatch = _.isEqual(expectedNewERC20BalancesByOwner, realERC20BalancesByOwner); - expect(erc20BalancesMatch, "Checking all known ERC20 account balances").to.be.true(); - + const areERC20BalancesEqual = _.isEqual(expectedNewERC20BalancesByOwner, realERC20BalancesByOwner); + expect(areERC20BalancesEqual, 'Checking all known ERC20 account balances').to.be.true(); // ERC721 Token Ids const sortedExpectedNewERC721TokenIdsByOwner = _.mapValues( expectedNewERC721TokenIdsByOwner, @@ -137,38 +151,12 @@ export class MatchOrderTester { _.sortBy(tokenIds); }); }); - const erc721TokenIdsMatch = _.isEqual( + const areERC721TokenIdsEqual = _.isEqual( sortedExpectedNewERC721TokenIdsByOwner, sortedNewERC721TokenIdsByOwner, ); - expect(erc721TokenIdsMatch, "Checking all known ERC721 account balances").to.be.true(); + expect(areERC721TokenIdsEqual, 'Checking all known ERC721 account balances').to.be.true(); } - - private async _verifyInitialOrderStates( - signedOrderLeft: SignedOrder, - signedOrderRight: SignedOrder, - initialTakerAssetFilledAmountLeft?: BigNumber, - initialTakerAssetFilledAmountRight?: BigNumber, - ): Promise<[BigNumber, BigNumber]> { - // Verify Left order preconditions - const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrderLeft), - ); - const expectedOrderFilledAmountLeft = initialTakerAssetFilledAmountLeft - ? initialTakerAssetFilledAmountLeft - : new BigNumber(0); - expect(expectedOrderFilledAmountLeft).to.be.bignumber.equal(orderTakerAssetFilledAmountLeft); - // Verify Right order preconditions - const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrderRight), - ); - const expectedOrderFilledAmountRight = initialTakerAssetFilledAmountRight - ? initialTakerAssetFilledAmountRight - : new BigNumber(0); - expect(expectedOrderFilledAmountRight).to.be.bignumber.equal(orderTakerAssetFilledAmountRight); - return [orderTakerAssetFilledAmountLeft, orderTakerAssetFilledAmountRight]; - } - /// @dev Constructs new MatchOrderTester. /// @param exchangeWrapper Used to call to the Exchange. /// @param erc20Wrapper Used to fetch ERC20 balances. @@ -208,23 +196,27 @@ export class MatchOrderTester { // Verify initial order states let orderTakerAssetFilledAmountLeft: BigNumber; let orderTakerAssetFilledAmountRight: BigNumber; - [orderTakerAssetFilledAmountLeft, orderTakerAssetFilledAmountRight] = await this._verifyInitialOrderStates( + [orderTakerAssetFilledAmountLeft, orderTakerAssetFilledAmountRight] = await this._verifyInitialOrderStatesAsync( signedOrderLeft, signedOrderRight, initialTakerAssetFilledAmountLeft, - initialTakerAssetFilledAmountRight + initialTakerAssetFilledAmountRight, ); // Match left & right orders - const transactionReceipt = await this._exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress); + const transactionReceipt = await this._exchangeWrapper.matchOrdersAsync( + signedOrderLeft, + signedOrderRight, + takerAddress, + ); const newERC20BalancesByOwner = await this._erc20Wrapper.getBalancesAsync(); const newERC721TokenIdsByOwner = await this._erc721Wrapper.getBalancesAsync(); // Verify logs - await this._verifyLogsAsync( + await MatchOrderTester._verifyLogsAsync( signedOrderLeft, signedOrderRight, transactionReceipt, takerAddress, - expectedTransferAmounts + expectedTransferAmounts, ); // Verify exchange state await this._verifyExchangeStateAsync( @@ -234,7 +226,7 @@ export class MatchOrderTester { orderTakerAssetFilledAmountRight, expectedTransferAmounts, initialTakerAssetFilledAmountLeft, - initialTakerAssetFilledAmountRight + initialTakerAssetFilledAmountRight, ); // Verify balances of makers, taker, and fee recipients await this._verifyBalancesAsync( @@ -245,57 +237,35 @@ export class MatchOrderTester { newERC20BalancesByOwner, newERC721TokenIdsByOwner, expectedTransferAmounts, - takerAddress + takerAddress, ); return [newERC20BalancesByOwner, newERC721TokenIdsByOwner]; } - /// @dev Calculates expected transfer amounts between order makers, fee recipients, and - /// the taker when two orders are matched. - /// @param signedOrderLeft First matched order. - /// @param signedOrderRight Second matched order. - /// @param orderTakerAssetFilledAmountLeft How much left order has been filled, prior to matching orders. - /// @param orderTakerAssetFilledAmountRight How much the right order has been filled, prior to matching orders. - /// @return TransferAmounts A struct containing the expected transfer amounts. - private async _verifyLogsAsync( + private async _verifyInitialOrderStatesAsync( signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder, - transactionReceipt: TransactionReceiptWithDecodedLogs, - takerAddress: string, - expectedTransferAmounts: TransferAmounts - ) { - // Parse logs - expect(transactionReceipt.logs.length, "Checking number of logs").to.be.equal(2); - // First log is for left fill - const leftLog = ((transactionReceipt.logs[0] as any) as {args: { makerAddress: string, takerAddress: string, makerAssetFilledAmount: string, takerAssetFilledAmount: string, makerFeePaid: string, takerFeePaid: string}}); - expect(leftLog.args.makerAddress, "Checking logged maker address of left order").to.be.equal(signedOrderLeft.makerAddress); - expect(leftLog.args.takerAddress, "Checking logged taker address of right order").to.be.equal(takerAddress); - const amountBoughtByLeftMaker = new BigNumber(leftLog.args.takerAssetFilledAmount); - const amountSoldByLeftMaker = new BigNumber(leftLog.args.makerAssetFilledAmount); - const feePaidByLeftMaker = new BigNumber(leftLog.args.makerFeePaid); - const feePaidByTakerLeft = new BigNumber(leftLog.args.takerFeePaid); - // Second log is for right fill - const rightLog = ((transactionReceipt.logs[1] as any) as {args: { makerAddress: string, takerAddress: string, makerAssetFilledAmount: string, takerAssetFilledAmount: string, makerFeePaid: string, takerFeePaid: string}}); - expect(rightLog.args.makerAddress, "Checking logged maker address of right order").to.be.equal(signedOrderRight.makerAddress); - expect(rightLog.args.takerAddress, "Checking loggerd taker address of right order").to.be.equal(takerAddress); - const amountBoughtByRightMaker = new BigNumber(rightLog.args.takerAssetFilledAmount); - const amountSoldByRightMaker = new BigNumber(rightLog.args.makerAssetFilledAmount); - const feePaidByRightMaker = new BigNumber(rightLog.args.makerFeePaid); - const feePaidByTakerRight = new BigNumber(rightLog.args.takerFeePaid); - // Derive amount received by taker - const amountReceivedByTaker = amountSoldByLeftMaker.sub(amountBoughtByRightMaker); - // Verify log values - left order - expect(expectedTransferAmounts.amountBoughtByLeftMaker, "Checking logged amount bought by left maker").to.be.bignumber.equal(amountBoughtByLeftMaker); - expect(expectedTransferAmounts.amountSoldByLeftMaker, "Checking logged amount sold by left maker").to.be.bignumber.equal(amountSoldByLeftMaker); - expect(expectedTransferAmounts.feePaidByLeftMaker, "Checking logged fee paid by left maker").to.be.bignumber.equal(feePaidByLeftMaker); - expect(expectedTransferAmounts.feePaidByTakerLeft, "Checking logged fee paid on left order by taker").to.be.bignumber.equal(feePaidByTakerLeft); - // Verify log values - right order - expect(expectedTransferAmounts.amountBoughtByRightMaker, "Checking logged amount bought by right maker").to.be.bignumber.equal(amountBoughtByRightMaker); - expect(expectedTransferAmounts.amountSoldByRightMaker, "Checking logged amount sold by right maker").to.be.bignumber.equal(amountSoldByRightMaker); - expect(expectedTransferAmounts.feePaidByRightMaker, "Checking logged fee paid by right maker").to.be.bignumber.equal(feePaidByRightMaker); - expect(expectedTransferAmounts.feePaidByTakerRight, "Checking logged fee paid on right order by taker").to.be.bignumber.equal(feePaidByTakerRight); - // Verify derived amount received by taker - expect(expectedTransferAmounts.amountReceivedByTaker, "Checking logged amount received by taker").to.be.bignumber.equal(amountReceivedByTaker); + initialTakerAssetFilledAmountLeft?: BigNumber, + initialTakerAssetFilledAmountRight?: BigNumber, + ): Promise<[BigNumber, BigNumber]> { + // Verify Left order preconditions + const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( + orderHashUtils.getOrderHashHex(signedOrderLeft), + ); + const expectedOrderFilledAmountLeft = initialTakerAssetFilledAmountLeft + ? initialTakerAssetFilledAmountLeft + : new BigNumber(0); + expect(expectedOrderFilledAmountLeft).to.be.bignumber.equal(orderTakerAssetFilledAmountLeft); + // Verify Right order preconditions + const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( + orderHashUtils.getOrderHashHex(signedOrderRight), + ); + const expectedOrderFilledAmountRight = initialTakerAssetFilledAmountRight + ? initialTakerAssetFilledAmountRight + : new BigNumber(0); + expect(expectedOrderFilledAmountRight).to.be.bignumber.equal(orderTakerAssetFilledAmountRight); + return [orderTakerAssetFilledAmountLeft, orderTakerAssetFilledAmountRight]; } + /// @dev Calculates expected transfer amounts between order makers, fee recipients, and /// the taker when two orders are matched. /// @param signedOrderLeft First matched order. @@ -310,34 +280,193 @@ export class MatchOrderTester { orderTakerAssetFilledAmountRight: BigNumber, expectedTransferAmounts: TransferAmounts, initialTakerAssetFilledAmountLeft?: BigNumber, - initialTakerAssetFilledAmountRight?: BigNumber - ) { + initialTakerAssetFilledAmountRight?: BigNumber, + ): Promise { // Verify state for left order: amount bought by left maker let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( orderHashUtils.getOrderHashHex(signedOrderLeft), ); amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(orderTakerAssetFilledAmountLeft); - expect(expectedTransferAmounts.amountBoughtByLeftMaker, "Checking exchange state for left order").to.be.bignumber.equal(amountBoughtByLeftMaker); + expect( + expectedTransferAmounts.amountBoughtByLeftMaker, + 'Checking exchange state for left order', + ).to.be.bignumber.equal(amountBoughtByLeftMaker); // Verify state for right order: amount bought by right maker let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( orderHashUtils.getOrderHashHex(signedOrderRight), ); amountBoughtByRightMaker = amountBoughtByRightMaker.minus(orderTakerAssetFilledAmountRight); - expect(expectedTransferAmounts.amountBoughtByRightMaker, "Checking exchange state for right order").to.be.bignumber.equal(amountBoughtByRightMaker); + expect( + expectedTransferAmounts.amountBoughtByRightMaker, + 'Checking exchange state for right order', + ).to.be.bignumber.equal(amountBoughtByRightMaker); // Verify left order status const maxAmountBoughtByLeftMaker = initialTakerAssetFilledAmountLeft ? signedOrderLeft.takerAssetAmount.sub(initialTakerAssetFilledAmountLeft) : signedOrderLeft.takerAssetAmount; const leftOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - const leftExpectedStatus = (expectedTransferAmounts.amountBoughtByLeftMaker.equals(maxAmountBoughtByLeftMaker)) ? OrderStatus.FULLY_FILLED : OrderStatus.FILLABLE; - expect(leftOrderInfo.orderStatus as OrderStatus, "Checking exchange status for left order").to.be.equal(leftExpectedStatus); + const leftExpectedStatus = expectedTransferAmounts.amountBoughtByLeftMaker.equals(maxAmountBoughtByLeftMaker) + ? OrderStatus.FULLY_FILLED + : OrderStatus.FILLABLE; + expect(leftOrderInfo.orderStatus as OrderStatus, 'Checking exchange status for left order').to.be.equal( + leftExpectedStatus, + ); // Verify right order status const maxAmountBoughtByRightMaker = initialTakerAssetFilledAmountRight ? signedOrderRight.takerAssetAmount.sub(initialTakerAssetFilledAmountRight) : signedOrderRight.takerAssetAmount; const rightOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderRight); - const rightExpectedStatus = (expectedTransferAmounts.amountBoughtByRightMaker.equals(maxAmountBoughtByRightMaker)) ? OrderStatus.FULLY_FILLED : OrderStatus.FILLABLE; - expect(rightOrderInfo.orderStatus as OrderStatus, "Checking exchange status for right order").to.be.equal(rightExpectedStatus); + const rightExpectedStatus = expectedTransferAmounts.amountBoughtByRightMaker.equals(maxAmountBoughtByRightMaker) + ? OrderStatus.FULLY_FILLED + : OrderStatus.FILLABLE; + expect(rightOrderInfo.orderStatus as OrderStatus, 'Checking exchange status for right order').to.be.equal( + rightExpectedStatus, + ); + } + private async _verifyBalancesAsync( + signedOrderLeft: SignedOrder, + signedOrderRight: SignedOrder, + initialERC20BalancesByOwner: ERC20BalancesByOwner, + initialERC721TokenIdsByOwner: ERC721TokenIdsByOwner, + finalERC20BalancesByOwner: ERC20BalancesByOwner, + finalERC721TokenIdsByOwner: ERC721TokenIdsByOwner, + expectedTransferAmounts: TransferAmounts, + takerAddress: string, + ): Promise { + let expectedERC20BalancesByOwner: ERC20BalancesByOwner; + let expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner; + [expectedERC20BalancesByOwner, expectedERC721TokenIdsByOwner] = this._calculateExpectedBalances( + signedOrderLeft, + signedOrderRight, + takerAddress, + initialERC20BalancesByOwner, + initialERC721TokenIdsByOwner, + expectedTransferAmounts, + ); + // Verify balances of makers, taker, and fee recipients + await this._verifyMakerTakerAndFeeRecipientBalancesAsync( + signedOrderLeft, + signedOrderRight, + expectedERC20BalancesByOwner, + finalERC20BalancesByOwner, + expectedERC721TokenIdsByOwner, + finalERC721TokenIdsByOwner, + takerAddress, + ); + // Verify balances for all known accounts + await MatchOrderTester._verifyAllKnownBalancesAsync( + expectedERC20BalancesByOwner, + finalERC20BalancesByOwner, + expectedERC721TokenIdsByOwner, + finalERC721TokenIdsByOwner, + ); + } + private async _verifyMakerTakerAndFeeRecipientBalancesAsync( + signedOrderLeft: SignedOrder, + signedOrderRight: SignedOrder, + expectedERC20BalancesByOwner: ERC20BalancesByOwner, + realERC20BalancesByOwner: ERC20BalancesByOwner, + expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner, + realERC721TokenIdsByOwner: ERC721TokenIdsByOwner, + takerAddress: string, + ): Promise { + // Individual balance comparisons + const makerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.makerAssetData); + const makerERC20AssetDataLeft = + makerAssetProxyIdLeft === AssetProxyId.ERC20 + ? assetDataUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData) + : assetDataUtils.decodeERC721AssetData(signedOrderLeft.makerAssetData); + const makerAssetAddressLeft = makerERC20AssetDataLeft.tokenAddress; + const makerAssetProxyIdRight = assetDataUtils.decodeAssetProxyId(signedOrderRight.makerAssetData); + const makerERC20AssetDataRight = + makerAssetProxyIdRight === AssetProxyId.ERC20 + ? assetDataUtils.decodeERC20AssetData(signedOrderRight.makerAssetData) + : assetDataUtils.decodeERC721AssetData(signedOrderRight.makerAssetData); + const makerAssetAddressRight = makerERC20AssetDataRight.tokenAddress; + if (makerAssetProxyIdLeft === AssetProxyId.ERC20) { + expect( + realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft], + 'Checking left maker egress ERC20 account balance', + ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]); + expect( + realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft], + 'Checking right maker ingress ERC20 account balance', + ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft]); + expect( + realERC20BalancesByOwner[takerAddress][makerAssetAddressLeft], + 'Checking taker ingress ERC20 account balance', + ).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][makerAssetAddressLeft]); + } else if (makerAssetProxyIdLeft === AssetProxyId.ERC721) { + expect( + realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort(), + 'Checking left maker egress ERC721 account holdings', + ).to.be.deep.equal( + expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort(), + ); + expect( + realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort(), + 'Checking right maker ERC721 account holdings', + ).to.be.deep.equal( + expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort(), + ); + expect( + realERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort(), + 'Checking taker ingress ERC721 account holdings', + ).to.be.deep.equal(expectedERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort()); + } else { + throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`); + } + if (makerAssetProxyIdRight === AssetProxyId.ERC20) { + expect( + realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight], + 'Checking left maker ingress ERC20 account balance', + ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]); + expect( + realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], + 'Checking right maker egress ERC20 account balance', + ).to.be.bignumber.equal( + expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], + ); + } else if (makerAssetProxyIdRight === AssetProxyId.ERC721) { + expect( + realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort(), + 'Checking left maker ingress ERC721 account holdings', + ).to.be.deep.equal( + expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort(), + ); + expect( + realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], + 'Checking right maker agress ERC721 account holdings', + ).to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]); + } else { + throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdRight}`); + } + // Paid fees + expect( + realERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress], + 'Checking left maker egress ERC20 account fees', + ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]); + expect( + realERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress], + 'Checking right maker egress ERC20 account fees', + ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]); + expect( + realERC20BalancesByOwner[takerAddress][this._feeTokenAddress], + 'Checking taker egress ERC20 account fees', + ).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][this._feeTokenAddress]); + // Received fees + expect( + realERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress], + 'Checking left fee recipient ingress ERC20 account fees', + ).to.be.bignumber.equal( + expectedERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress], + ); + expect( + realERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress], + 'Checking right fee receipient ingress ERC20 account fees', + ).to.be.bignumber.equal( + expectedERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress], + ); } /// @dev Calculates the expected balances of order makers, fee recipients, and the taker, /// as a result of matching two orders. @@ -438,7 +567,9 @@ export class MatchOrderTester { // Taker Fees expectedNewERC20BalancesByOwner[takerAddress][this._feeTokenAddress] = expectedNewERC20BalancesByOwner[ takerAddress - ][this._feeTokenAddress].minus(expectedTransferAmounts.feePaidByTakerLeft.add(expectedTransferAmounts.feePaidByTakerRight)); + ][this._feeTokenAddress].minus( + expectedTransferAmounts.feePaidByTakerLeft.add(expectedTransferAmounts.feePaidByTakerRight), + ); // Left Fee Recipient Fees expectedNewERC20BalancesByOwner[feeRecipientAddressLeft][ this._feeTokenAddress @@ -454,4 +585,4 @@ export class MatchOrderTester { return [expectedNewERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner]; } -} +} // tslint:disable-line:max-file-line-count diff --git a/packages/contracts/test/utils/types.ts b/packages/contracts/test/utils/types.ts index 481ee87d6..172622777 100644 --- a/packages/contracts/test/utils/types.ts +++ b/packages/contracts/test/utils/types.ts @@ -117,21 +117,21 @@ export interface TransferAmountsByMatchOrders { // Left Maker amountBoughtByLeftMaker: BigNumber; amountSoldByLeftMaker: BigNumber; - amountReceivedByLeftMaker: BigNumber; + // amountReceivedByLeftMaker: BigNumber; feePaidByLeftMaker: BigNumber; // Right Maker amountBoughtByRightMaker: BigNumber; amountSoldByRightMaker: BigNumber; - amountReceivedByRightMaker: BigNumber; + // amountReceivedByRightMaker: BigNumber; feePaidByRightMaker: BigNumber; // Taker amountReceivedByTaker: BigNumber; feePaidByTakerLeft: BigNumber; feePaidByTakerRight: BigNumber; - totalFeePaidByTaker: BigNumber; + // totalFeePaidByTaker: BigNumber; // Fee Recipients - feeReceivedLeft: BigNumber; - feeReceivedRight: BigNumber; + // feeReceivedLeft: BigNumber; + // feeReceivedRight: BigNumber; } export interface OrderInfo { -- cgit From ac872e518197573fddaded73745e58b4235f8af3 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 23 Aug 2018 10:40:02 -0700 Subject: Added `expect` messages for checking left/right order states --- packages/contracts/test/utils/match_order_tester.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index 300073eb0..98bf80011 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -254,7 +254,9 @@ export class MatchOrderTester { const expectedOrderFilledAmountLeft = initialTakerAssetFilledAmountLeft ? initialTakerAssetFilledAmountLeft : new BigNumber(0); - expect(expectedOrderFilledAmountLeft).to.be.bignumber.equal(orderTakerAssetFilledAmountLeft); + expect(expectedOrderFilledAmountLeft, 'Checking inital state of left order').to.be.bignumber.equal( + orderTakerAssetFilledAmountLeft, + ); // Verify Right order preconditions const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( orderHashUtils.getOrderHashHex(signedOrderRight), @@ -262,7 +264,9 @@ export class MatchOrderTester { const expectedOrderFilledAmountRight = initialTakerAssetFilledAmountRight ? initialTakerAssetFilledAmountRight : new BigNumber(0); - expect(expectedOrderFilledAmountRight).to.be.bignumber.equal(orderTakerAssetFilledAmountRight); + expect(expectedOrderFilledAmountRight, 'Checking inital state of right order').to.be.bignumber.equal( + orderTakerAssetFilledAmountRight, + ); return [orderTakerAssetFilledAmountLeft, orderTakerAssetFilledAmountRight]; } -- cgit From 9a5ec8d030c9e9b08018abac54a8d16933affabc Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 23 Aug 2018 11:38:27 -0700 Subject: Added function signature comments --- packages/contracts/test/exchange/match_orders.ts | 54 +-- .../contracts/test/utils/match_order_tester.ts | 403 ++++++++++----------- packages/contracts/test/utils/types.ts | 15 +- 3 files changed, 233 insertions(+), 239 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index 7e9776d52..58b8250ab 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -228,15 +228,15 @@ describe.only('matchOrders', () => { // Left Maker amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 0), amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), // 100% + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Right Maker amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 16), // 75% // Taker amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(9), 0), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 17), // 50% + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 16), // 50% }; // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndVerifyBalancesAsync( @@ -300,8 +300,8 @@ describe.only('matchOrders', () => { feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Taker amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, @@ -335,8 +335,8 @@ describe.only('matchOrders', () => { feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Taker amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndVerifyBalancesAsync( @@ -371,8 +371,8 @@ describe.only('matchOrders', () => { feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 16), // 50% // Taker amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 40% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 16), // 100% + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 16), // 50% }; // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndVerifyBalancesAsync( @@ -407,8 +407,8 @@ describe.only('matchOrders', () => { feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Taker amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 50% + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 16), // 10% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndVerifyBalancesAsync( @@ -445,8 +445,8 @@ describe.only('matchOrders', () => { feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Taker amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 50% + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 16), // 10% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; // prettier-ignore [ @@ -604,8 +604,8 @@ describe.only('matchOrders', () => { feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Taker amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, @@ -640,8 +640,8 @@ describe.only('matchOrders', () => { feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Taker amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, @@ -676,8 +676,8 @@ describe.only('matchOrders', () => { feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Taker amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, @@ -712,8 +712,8 @@ describe.only('matchOrders', () => { feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Taker amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, @@ -748,8 +748,8 @@ describe.only('matchOrders', () => { feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Taker amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, @@ -783,8 +783,8 @@ describe.only('matchOrders', () => { feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Taker amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 18), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, @@ -929,7 +929,7 @@ describe.only('matchOrders', () => { ); }); - it.only('should transfer correct amounts when right order maker asset is an ERC721 token', async () => { + it('should transfer correct amounts when right order maker asset is an ERC721 token', async () => { // Create orders to match const erc721TokenToTransfer = erc721RightMakerAssetIds[0]; const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ @@ -955,7 +955,7 @@ describe.only('matchOrders', () => { // Taker amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 50% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; await matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index 98bf80011..7d763c6df 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -16,6 +16,7 @@ import { OrderInfo, OrderStatus, TransferAmountsByMatchOrders as TransferAmounts, + TransferAmountsLoggedByMatchOrders as LoggedTransferAmounts, } from './types'; chaiSetup.configure(); @@ -26,13 +27,14 @@ export class MatchOrderTester { private readonly _erc20Wrapper: ERC20Wrapper; private readonly _erc721Wrapper: ERC721Wrapper; private readonly _feeTokenAddress: string; - /// @dev Calculates expected transfer amounts between order makers, fee recipients, and - /// the taker when two orders are matched. + /// @dev Checks values from the logs produced by Exchange.matchOrders against the expected transfer amounts. + /// Values include the amounts transferred from the left/right makers and taker, along with + /// the fees paid on each matched order. These are also the return values of MatchOrders. /// @param signedOrderLeft First matched order. /// @param signedOrderRight Second matched order. - /// @param orderTakerAssetFilledAmountLeft How much left order has been filled, prior to matching orders. - /// @param orderTakerAssetFilledAmountRight How much the right order has been filled, prior to matching orders. - /// @return TransferAmounts A struct containing the expected transfer amounts. + /// @param transactionReceipt Transaction receipt and logs produced by Exchange.matchOrders. + /// @param takerAddress Address of taker (account that called Exchange.matchOrders) + /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching. private static async _verifyLogsAsync( signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder, @@ -40,46 +42,28 @@ export class MatchOrderTester { takerAddress: string, expectedTransferAmounts: TransferAmounts, ): Promise { - // Parse logs + // Should have two logs -- one for each order. expect(transactionReceipt.logs.length, 'Checking number of logs').to.be.equal(2); // First log is for left fill - const leftLog = (transactionReceipt.logs[0] as any) as { - args: { - makerAddress: string; - takerAddress: string; - makerAssetFilledAmount: string; - takerAssetFilledAmount: string; - makerFeePaid: string; - takerFeePaid: string; - }; - }; - expect(leftLog.args.makerAddress, 'Checking logged maker address of left order').to.be.equal( + const leftLog = (transactionReceipt.logs[0] as any).args as LoggedTransferAmounts; + expect(leftLog.makerAddress, 'Checking logged maker address of left order').to.be.equal( signedOrderLeft.makerAddress, ); - expect(leftLog.args.takerAddress, 'Checking logged taker address of right order').to.be.equal(takerAddress); - const amountBoughtByLeftMaker = new BigNumber(leftLog.args.takerAssetFilledAmount); - const amountSoldByLeftMaker = new BigNumber(leftLog.args.makerAssetFilledAmount); - const feePaidByLeftMaker = new BigNumber(leftLog.args.makerFeePaid); - const feePaidByTakerLeft = new BigNumber(leftLog.args.takerFeePaid); + expect(leftLog.takerAddress, 'Checking logged taker address of right order').to.be.equal(takerAddress); + const amountBoughtByLeftMaker = new BigNumber(leftLog.takerAssetFilledAmount); + const amountSoldByLeftMaker = new BigNumber(leftLog.makerAssetFilledAmount); + const feePaidByLeftMaker = new BigNumber(leftLog.makerFeePaid); + const feePaidByTakerLeft = new BigNumber(leftLog.takerFeePaid); // Second log is for right fill - const rightLog = (transactionReceipt.logs[1] as any) as { - args: { - makerAddress: string; - takerAddress: string; - makerAssetFilledAmount: string; - takerAssetFilledAmount: string; - makerFeePaid: string; - takerFeePaid: string; - }; - }; - expect(rightLog.args.makerAddress, 'Checking logged maker address of right order').to.be.equal( + const rightLog = (transactionReceipt.logs[1] as any).args as LoggedTransferAmounts; + expect(rightLog.makerAddress, 'Checking logged maker address of right order').to.be.equal( signedOrderRight.makerAddress, ); - expect(rightLog.args.takerAddress, 'Checking loggerd taker address of right order').to.be.equal(takerAddress); - const amountBoughtByRightMaker = new BigNumber(rightLog.args.takerAssetFilledAmount); - const amountSoldByRightMaker = new BigNumber(rightLog.args.makerAssetFilledAmount); - const feePaidByRightMaker = new BigNumber(rightLog.args.makerFeePaid); - const feePaidByTakerRight = new BigNumber(rightLog.args.takerFeePaid); + expect(rightLog.takerAddress, 'Checking loggerd taker address of right order').to.be.equal(takerAddress); + const amountBoughtByRightMaker = new BigNumber(rightLog.takerAssetFilledAmount); + const amountSoldByRightMaker = new BigNumber(rightLog.makerAssetFilledAmount); + const feePaidByRightMaker = new BigNumber(rightLog.makerFeePaid); + const feePaidByTakerRight = new BigNumber(rightLog.takerFeePaid); // Derive amount received by taker const amountReceivedByTaker = amountSoldByLeftMaker.sub(amountBoughtByRightMaker); // Verify log values - left order @@ -122,30 +106,26 @@ export class MatchOrderTester { 'Checking logged amount received by taker', ).to.be.bignumber.equal(amountReceivedByTaker); } - /// @dev Compares a pair of ERC20 balances and a pair of ERC721 token owners. - /// @param expectedNewERC20BalancesByOwner Expected ERC20 balances. - /// @param realERC20BalancesByOwner Actual ERC20 balances. - /// @param expectedNewERC721TokenIdsByOwner Expected ERC721 token owners. - /// @param realERC721TokenIdsByOwner Actual ERC20 token owners. - /// @return True only if ERC20 balances match and ERC721 token owners match. + /// @dev Verifies all expected ERC20 and ERC721 account holdings match the real holdings. + /// @param expectedERC20BalancesByOwner Expected ERC20 balances. + /// @param realERC20BalancesByOwner Real ERC20 balances. + /// @param expectedERC721TokenIdsByOwner Expected ERC721 token owners. + /// @param realERC721TokenIdsByOwner Real ERC20 token owners. private static async _verifyAllKnownBalancesAsync( - expectedNewERC20BalancesByOwner: ERC20BalancesByOwner, + expectedERC20BalancesByOwner: ERC20BalancesByOwner, realERC20BalancesByOwner: ERC20BalancesByOwner, - expectedNewERC721TokenIdsByOwner: ERC721TokenIdsByOwner, + expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner, realERC721TokenIdsByOwner: ERC721TokenIdsByOwner, ): Promise { // ERC20 Balances - const areERC20BalancesEqual = _.isEqual(expectedNewERC20BalancesByOwner, realERC20BalancesByOwner); + const areERC20BalancesEqual = _.isEqual(expectedERC20BalancesByOwner, realERC20BalancesByOwner); expect(areERC20BalancesEqual, 'Checking all known ERC20 account balances').to.be.true(); // ERC721 Token Ids - const sortedExpectedNewERC721TokenIdsByOwner = _.mapValues( - expectedNewERC721TokenIdsByOwner, - tokenIdsByOwner => { - _.mapValues(tokenIdsByOwner, tokenIds => { - _.sortBy(tokenIds); - }); - }, - ); + const sortedExpectedNewERC721TokenIdsByOwner = _.mapValues(expectedERC721TokenIdsByOwner, tokenIdsByOwner => { + _.mapValues(tokenIdsByOwner, tokenIds => { + _.sortBy(tokenIds); + }); + }); const sortedNewERC721TokenIdsByOwner = _.mapValues(realERC721TokenIdsByOwner, tokenIdsByOwner => { _.mapValues(tokenIdsByOwner, tokenIds => { _.sortBy(tokenIds); @@ -173,15 +153,16 @@ export class MatchOrderTester { this._erc721Wrapper = erc721Wrapper; this._feeTokenAddress = feeTokenAddress; } - /// @dev Matches two complementary orders and validates results. + /// @dev Matches two complementary orders and verifies results. /// Validation either succeeds or throws. /// @param signedOrderLeft First matched order. /// @param signedOrderRight Second matched order. /// @param takerAddress Address of taker (the address who matched the two orders) /// @param erc20BalancesByOwner Current ERC20 balances. /// @param erc721TokenIdsByOwner Current ERC721 token owners. - /// @param initialTakerAssetFilledAmountLeft Current amount the left order has been filled. - /// @param initialTakerAssetFilledAmountRight Current amount the right order has been filled. + /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching. + /// @param initialLeftOrderFilledAmount How much left order has been filled, prior to matching orders. + /// @param initialRightOrderFilledAmount How much the right order has been filled, prior to matching orders. /// @return New ERC20 balances & ERC721 token owners. public async matchOrdersAndVerifyBalancesAsync( signedOrderLeft: SignedOrder, @@ -190,17 +171,22 @@ export class MatchOrderTester { erc20BalancesByOwner: ERC20BalancesByOwner, erc721TokenIdsByOwner: ERC721TokenIdsByOwner, expectedTransferAmounts: TransferAmounts, - initialTakerAssetFilledAmountLeft?: BigNumber, - initialTakerAssetFilledAmountRight?: BigNumber, + initialLeftOrderFilledAmount?: BigNumber, + initialRightOrderFilledAmount?: BigNumber, ): Promise<[ERC20BalancesByOwner, ERC721TokenIdsByOwner]> { + // Assign default values to optional parameters if undefined + if (initialLeftOrderFilledAmount === undefined) { + initialLeftOrderFilledAmount = new BigNumber(0); + } + if (initialRightOrderFilledAmount === undefined) { + initialRightOrderFilledAmount = new BigNumber(0); + } // Verify initial order states - let orderTakerAssetFilledAmountLeft: BigNumber; - let orderTakerAssetFilledAmountRight: BigNumber; - [orderTakerAssetFilledAmountLeft, orderTakerAssetFilledAmountRight] = await this._verifyInitialOrderStatesAsync( + await this._verifyInitialOrderStatesAsync( signedOrderLeft, signedOrderRight, - initialTakerAssetFilledAmountLeft, - initialTakerAssetFilledAmountRight, + initialLeftOrderFilledAmount, + initialRightOrderFilledAmount, ); // Match left & right orders const transactionReceipt = await this._exchangeWrapper.matchOrdersAsync( @@ -222,11 +208,9 @@ export class MatchOrderTester { await this._verifyExchangeStateAsync( signedOrderLeft, signedOrderRight, - orderTakerAssetFilledAmountLeft, - orderTakerAssetFilledAmountRight, + initialLeftOrderFilledAmount, + initialRightOrderFilledAmount, expectedTransferAmounts, - initialTakerAssetFilledAmountLeft, - initialTakerAssetFilledAmountRight, ); // Verify balances of makers, taker, and fee recipients await this._verifyBalancesAsync( @@ -241,56 +225,50 @@ export class MatchOrderTester { ); return [newERC20BalancesByOwner, newERC721TokenIdsByOwner]; } + /// @dev Verifies initial exchange state for the left and right orders. + /// @param signedOrderLeft First matched order. + /// @param signedOrderRight Second matched order. + /// @param expectedOrderFilledAmountLeft How much left order has been filled, prior to matching orders. + /// @param expectedOrderFilledAmountRight How much the right order has been filled, prior to matching orders. private async _verifyInitialOrderStatesAsync( signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder, - initialTakerAssetFilledAmountLeft?: BigNumber, - initialTakerAssetFilledAmountRight?: BigNumber, - ): Promise<[BigNumber, BigNumber]> { - // Verify Left order preconditions + expectedOrderFilledAmountLeft: BigNumber, + expectedOrderFilledAmountRight: BigNumber, + ): Promise { + // Verify left order initial state const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( orderHashUtils.getOrderHashHex(signedOrderLeft), ); - const expectedOrderFilledAmountLeft = initialTakerAssetFilledAmountLeft - ? initialTakerAssetFilledAmountLeft - : new BigNumber(0); expect(expectedOrderFilledAmountLeft, 'Checking inital state of left order').to.be.bignumber.equal( orderTakerAssetFilledAmountLeft, ); - // Verify Right order preconditions + // Verify right order initial state const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( orderHashUtils.getOrderHashHex(signedOrderRight), ); - const expectedOrderFilledAmountRight = initialTakerAssetFilledAmountRight - ? initialTakerAssetFilledAmountRight - : new BigNumber(0); expect(expectedOrderFilledAmountRight, 'Checking inital state of right order').to.be.bignumber.equal( orderTakerAssetFilledAmountRight, ); - return [orderTakerAssetFilledAmountLeft, orderTakerAssetFilledAmountRight]; } - - /// @dev Calculates expected transfer amounts between order makers, fee recipients, and - /// the taker when two orders are matched. + /// @dev Verifies the exchange state against the expected amounts transferred by from matching orders. /// @param signedOrderLeft First matched order. /// @param signedOrderRight Second matched order. - /// @param orderTakerAssetFilledAmountLeft How much left order has been filled, prior to matching orders. - /// @param orderTakerAssetFilledAmountRight How much the right order has been filled, prior to matching orders. + /// @param initialLeftOrderFilledAmount How much left order has been filled, prior to matching orders. + /// @param initialRightOrderFilledAmount How much the right order has been filled, prior to matching orders. /// @return TransferAmounts A struct containing the expected transfer amounts. private async _verifyExchangeStateAsync( signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder, - orderTakerAssetFilledAmountLeft: BigNumber, - orderTakerAssetFilledAmountRight: BigNumber, + initialLeftOrderFilledAmount: BigNumber, + initialRightOrderFilledAmount: BigNumber, expectedTransferAmounts: TransferAmounts, - initialTakerAssetFilledAmountLeft?: BigNumber, - initialTakerAssetFilledAmountRight?: BigNumber, ): Promise { // Verify state for left order: amount bought by left maker let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( orderHashUtils.getOrderHashHex(signedOrderLeft), ); - amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(orderTakerAssetFilledAmountLeft); + amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(initialLeftOrderFilledAmount); expect( expectedTransferAmounts.amountBoughtByLeftMaker, 'Checking exchange state for left order', @@ -299,15 +277,13 @@ export class MatchOrderTester { let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( orderHashUtils.getOrderHashHex(signedOrderRight), ); - amountBoughtByRightMaker = amountBoughtByRightMaker.minus(orderTakerAssetFilledAmountRight); + amountBoughtByRightMaker = amountBoughtByRightMaker.minus(initialRightOrderFilledAmount); expect( expectedTransferAmounts.amountBoughtByRightMaker, 'Checking exchange state for right order', ).to.be.bignumber.equal(amountBoughtByRightMaker); // Verify left order status - const maxAmountBoughtByLeftMaker = initialTakerAssetFilledAmountLeft - ? signedOrderLeft.takerAssetAmount.sub(initialTakerAssetFilledAmountLeft) - : signedOrderLeft.takerAssetAmount; + const maxAmountBoughtByLeftMaker = signedOrderLeft.takerAssetAmount.minus(initialLeftOrderFilledAmount); const leftOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderLeft); const leftExpectedStatus = expectedTransferAmounts.amountBoughtByLeftMaker.equals(maxAmountBoughtByLeftMaker) ? OrderStatus.FULLY_FILLED @@ -316,9 +292,7 @@ export class MatchOrderTester { leftExpectedStatus, ); // Verify right order status - const maxAmountBoughtByRightMaker = initialTakerAssetFilledAmountRight - ? signedOrderRight.takerAssetAmount.sub(initialTakerAssetFilledAmountRight) - : signedOrderRight.takerAssetAmount; + const maxAmountBoughtByRightMaker = signedOrderRight.takerAssetAmount.minus(initialRightOrderFilledAmount); const rightOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderRight); const rightExpectedStatus = expectedTransferAmounts.amountBoughtByRightMaker.equals(maxAmountBoughtByRightMaker) ? OrderStatus.FULLY_FILLED @@ -327,6 +301,15 @@ export class MatchOrderTester { rightExpectedStatus, ); } + /// @dev Verifies account balances after matching orders. + /// @param signedOrderLeft First matched order. + /// @param signedOrderRight Second matched order. + /// @param initialERC20BalancesByOwner ERC20 balances prior to order matching. + /// @param initialERC721TokenIdsByOwner ERC721 token owners prior to order matching. + /// @param finalERC20BalancesByOwner ERC20 balances after order matching. + /// @param finalERC721TokenIdsByOwner ERC721 token owners after order matching. + /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching. + /// @param takerAddress Address of taker (account that called Exchange.matchOrders). private async _verifyBalancesAsync( signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder, @@ -365,113 +348,6 @@ export class MatchOrderTester { finalERC721TokenIdsByOwner, ); } - private async _verifyMakerTakerAndFeeRecipientBalancesAsync( - signedOrderLeft: SignedOrder, - signedOrderRight: SignedOrder, - expectedERC20BalancesByOwner: ERC20BalancesByOwner, - realERC20BalancesByOwner: ERC20BalancesByOwner, - expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner, - realERC721TokenIdsByOwner: ERC721TokenIdsByOwner, - takerAddress: string, - ): Promise { - // Individual balance comparisons - const makerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.makerAssetData); - const makerERC20AssetDataLeft = - makerAssetProxyIdLeft === AssetProxyId.ERC20 - ? assetDataUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData) - : assetDataUtils.decodeERC721AssetData(signedOrderLeft.makerAssetData); - const makerAssetAddressLeft = makerERC20AssetDataLeft.tokenAddress; - const makerAssetProxyIdRight = assetDataUtils.decodeAssetProxyId(signedOrderRight.makerAssetData); - const makerERC20AssetDataRight = - makerAssetProxyIdRight === AssetProxyId.ERC20 - ? assetDataUtils.decodeERC20AssetData(signedOrderRight.makerAssetData) - : assetDataUtils.decodeERC721AssetData(signedOrderRight.makerAssetData); - const makerAssetAddressRight = makerERC20AssetDataRight.tokenAddress; - if (makerAssetProxyIdLeft === AssetProxyId.ERC20) { - expect( - realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft], - 'Checking left maker egress ERC20 account balance', - ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]); - expect( - realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft], - 'Checking right maker ingress ERC20 account balance', - ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft]); - expect( - realERC20BalancesByOwner[takerAddress][makerAssetAddressLeft], - 'Checking taker ingress ERC20 account balance', - ).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][makerAssetAddressLeft]); - } else if (makerAssetProxyIdLeft === AssetProxyId.ERC721) { - expect( - realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort(), - 'Checking left maker egress ERC721 account holdings', - ).to.be.deep.equal( - expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort(), - ); - expect( - realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort(), - 'Checking right maker ERC721 account holdings', - ).to.be.deep.equal( - expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort(), - ); - expect( - realERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort(), - 'Checking taker ingress ERC721 account holdings', - ).to.be.deep.equal(expectedERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort()); - } else { - throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`); - } - if (makerAssetProxyIdRight === AssetProxyId.ERC20) { - expect( - realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight], - 'Checking left maker ingress ERC20 account balance', - ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]); - expect( - realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], - 'Checking right maker egress ERC20 account balance', - ).to.be.bignumber.equal( - expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], - ); - } else if (makerAssetProxyIdRight === AssetProxyId.ERC721) { - expect( - realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort(), - 'Checking left maker ingress ERC721 account holdings', - ).to.be.deep.equal( - expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort(), - ); - expect( - realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], - 'Checking right maker agress ERC721 account holdings', - ).to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]); - } else { - throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdRight}`); - } - // Paid fees - expect( - realERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress], - 'Checking left maker egress ERC20 account fees', - ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]); - expect( - realERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress], - 'Checking right maker egress ERC20 account fees', - ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]); - expect( - realERC20BalancesByOwner[takerAddress][this._feeTokenAddress], - 'Checking taker egress ERC20 account fees', - ).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][this._feeTokenAddress]); - // Received fees - expect( - realERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress], - 'Checking left fee recipient ingress ERC20 account fees', - ).to.be.bignumber.equal( - expectedERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress], - ); - expect( - realERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress], - 'Checking right fee receipient ingress ERC20 account fees', - ).to.be.bignumber.equal( - expectedERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress], - ); - } /// @dev Calculates the expected balances of order makers, fee recipients, and the taker, /// as a result of matching two orders. /// @param signedOrderRight First matched order. @@ -479,7 +355,7 @@ export class MatchOrderTester { /// @param takerAddress Address of taker (the address who matched the two orders) /// @param erc20BalancesByOwner Current ERC20 balances. /// @param erc721TokenIdsByOwner Current ERC721 token owners. - /// @param expectedTransferAmounts A struct containing the expected transfer amounts. + /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching. /// @return Expected ERC20 balances & ERC721 token owners after orders have been matched. private _calculateExpectedBalances( signedOrderLeft: SignedOrder, @@ -589,4 +465,119 @@ export class MatchOrderTester { return [expectedNewERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner]; } + /// @dev + /// @param signedOrderLeft First matched order. + /// @param signedOrderRight Second matched order. + /// @param expectedERC20BalancesByOwner Expected ERC20 balances. + /// @param realERC20BalancesByOwner Real ERC20 balances. + /// @param expectedERC721TokenIdsByOwner Expected ERC721 token owners. + /// @param realERC721TokenIdsByOwner Real ERC20 token owners. + /// @param takerAddress Address of taker (account that called Exchange.matchOrders). + private async _verifyMakerTakerAndFeeRecipientBalancesAsync( + signedOrderLeft: SignedOrder, + signedOrderRight: SignedOrder, + expectedERC20BalancesByOwner: ERC20BalancesByOwner, + realERC20BalancesByOwner: ERC20BalancesByOwner, + expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner, + realERC721TokenIdsByOwner: ERC721TokenIdsByOwner, + takerAddress: string, + ): Promise { + // Individual balance comparisons + const makerAssetProxyIdLeft = assetDataUtils.decodeAssetProxyId(signedOrderLeft.makerAssetData); + const makerERC20AssetDataLeft = + makerAssetProxyIdLeft === AssetProxyId.ERC20 + ? assetDataUtils.decodeERC20AssetData(signedOrderLeft.makerAssetData) + : assetDataUtils.decodeERC721AssetData(signedOrderLeft.makerAssetData); + const makerAssetAddressLeft = makerERC20AssetDataLeft.tokenAddress; + const makerAssetProxyIdRight = assetDataUtils.decodeAssetProxyId(signedOrderRight.makerAssetData); + const makerERC20AssetDataRight = + makerAssetProxyIdRight === AssetProxyId.ERC20 + ? assetDataUtils.decodeERC20AssetData(signedOrderRight.makerAssetData) + : assetDataUtils.decodeERC721AssetData(signedOrderRight.makerAssetData); + const makerAssetAddressRight = makerERC20AssetDataRight.tokenAddress; + if (makerAssetProxyIdLeft === AssetProxyId.ERC20) { + expect( + realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft], + 'Checking left maker egress ERC20 account balance', + ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft]); + expect( + realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft], + 'Checking right maker ingress ERC20 account balance', + ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft]); + expect( + realERC20BalancesByOwner[takerAddress][makerAssetAddressLeft], + 'Checking taker ingress ERC20 account balance', + ).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][makerAssetAddressLeft]); + } else if (makerAssetProxyIdLeft === AssetProxyId.ERC721) { + expect( + realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort(), + 'Checking left maker egress ERC721 account holdings', + ).to.be.deep.equal( + expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressLeft].sort(), + ); + expect( + realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort(), + 'Checking right maker ERC721 account holdings', + ).to.be.deep.equal( + expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressLeft].sort(), + ); + expect( + realERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort(), + 'Checking taker ingress ERC721 account holdings', + ).to.be.deep.equal(expectedERC721TokenIdsByOwner[takerAddress][makerAssetAddressLeft].sort()); + } else { + throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdLeft}`); + } + if (makerAssetProxyIdRight === AssetProxyId.ERC20) { + expect( + realERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight], + 'Checking left maker ingress ERC20 account balance', + ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight]); + expect( + realERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], + 'Checking right maker egress ERC20 account balance', + ).to.be.bignumber.equal( + expectedERC20BalancesByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], + ); + } else if (makerAssetProxyIdRight === AssetProxyId.ERC721) { + expect( + realERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort(), + 'Checking left maker ingress ERC721 account holdings', + ).to.be.deep.equal( + expectedERC721TokenIdsByOwner[signedOrderLeft.makerAddress][makerAssetAddressRight].sort(), + ); + expect( + realERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight], + 'Checking right maker agress ERC721 account holdings', + ).to.be.deep.equal(expectedERC721TokenIdsByOwner[signedOrderRight.makerAddress][makerAssetAddressRight]); + } else { + throw new Error(`Unhandled Asset Proxy ID: ${makerAssetProxyIdRight}`); + } + // Paid fees + expect( + realERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress], + 'Checking left maker egress ERC20 account fees', + ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderLeft.makerAddress][this._feeTokenAddress]); + expect( + realERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress], + 'Checking right maker egress ERC20 account fees', + ).to.be.bignumber.equal(expectedERC20BalancesByOwner[signedOrderRight.makerAddress][this._feeTokenAddress]); + expect( + realERC20BalancesByOwner[takerAddress][this._feeTokenAddress], + 'Checking taker egress ERC20 account fees', + ).to.be.bignumber.equal(expectedERC20BalancesByOwner[takerAddress][this._feeTokenAddress]); + // Received fees + expect( + realERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress], + 'Checking left fee recipient ingress ERC20 account fees', + ).to.be.bignumber.equal( + expectedERC20BalancesByOwner[signedOrderLeft.feeRecipientAddress][this._feeTokenAddress], + ); + expect( + realERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress], + 'Checking right fee receipient ingress ERC20 account fees', + ).to.be.bignumber.equal( + expectedERC20BalancesByOwner[signedOrderRight.feeRecipientAddress][this._feeTokenAddress], + ); + } } // tslint:disable-line:max-file-line-count diff --git a/packages/contracts/test/utils/types.ts b/packages/contracts/test/utils/types.ts index 172622777..598fb5393 100644 --- a/packages/contracts/test/utils/types.ts +++ b/packages/contracts/test/utils/types.ts @@ -117,21 +117,24 @@ export interface TransferAmountsByMatchOrders { // Left Maker amountBoughtByLeftMaker: BigNumber; amountSoldByLeftMaker: BigNumber; - // amountReceivedByLeftMaker: BigNumber; feePaidByLeftMaker: BigNumber; // Right Maker amountBoughtByRightMaker: BigNumber; amountSoldByRightMaker: BigNumber; - // amountReceivedByRightMaker: BigNumber; feePaidByRightMaker: BigNumber; // Taker amountReceivedByTaker: BigNumber; feePaidByTakerLeft: BigNumber; feePaidByTakerRight: BigNumber; - // totalFeePaidByTaker: BigNumber; - // Fee Recipients - // feeReceivedLeft: BigNumber; - // feeReceivedRight: BigNumber; +} + +export interface TransferAmountsLoggedByMatchOrders { + makerAddress: string; + takerAddress: string; + makerAssetFilledAmount: string; + takerAssetFilledAmount: string; + makerFeePaid: string; + takerFeePaid: string; } export interface OrderInfo { -- cgit From 9fcb2dda73ed7fbcf772364029b987fb147d7387 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 23 Aug 2018 11:39:59 -0700 Subject: Fixed a function comment --- packages/contracts/test/utils/match_order_tester.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index 7d763c6df..ee298097d 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -465,7 +465,8 @@ export class MatchOrderTester { return [expectedNewERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner]; } - /// @dev + /// @dev Verifies ERC20 account balances and ERC721 token holdings that result from order matching. + /// Specifically checks balances of makers, taker and fee recipients. /// @param signedOrderLeft First matched order. /// @param signedOrderRight Second matched order. /// @param expectedERC20BalancesByOwner Expected ERC20 balances. -- cgit From 1e6b83719a6de17f4501b13afe3e8bef82087def Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 23 Aug 2018 11:45:20 -0700 Subject: Renaming verify -> assert in order matching --- packages/contracts/test/exchange/match_orders.ts | 36 +++++------ .../contracts/test/utils/match_order_tester.ts | 69 +++++++++++----------- 2 files changed, 52 insertions(+), 53 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index 58b8250ab..95d4b7502 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -191,7 +191,7 @@ describe.only('matchOrders', () => { feeRecipientAddress: feeRecipientAddressRight, }); // Match signedOrderLeft with signedOrderRight - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -239,7 +239,7 @@ describe.only('matchOrders', () => { feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 16), // 50% }; // Match signedOrderLeft with signedOrderRight - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -303,7 +303,7 @@ describe.only('matchOrders', () => { feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -339,7 +339,7 @@ describe.only('matchOrders', () => { feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; // Match signedOrderLeft with signedOrderRight - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -375,7 +375,7 @@ describe.only('matchOrders', () => { feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 16), // 50% }; // Match signedOrderLeft with signedOrderRight - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -411,7 +411,7 @@ describe.only('matchOrders', () => { feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; // Match signedOrderLeft with signedOrderRight - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -453,7 +453,7 @@ describe.only('matchOrders', () => { newERC20BalancesByOwner, // tslint:disable-next-line:trailing-comma newERC721TokenIdsByOwner - ] = await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + ] = await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -486,7 +486,7 @@ describe.only('matchOrders', () => { feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 16), // 90% (10% paid earlier) feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 16), // 90% }; - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight2, takerAddress, @@ -531,7 +531,7 @@ describe.only('matchOrders', () => { newERC20BalancesByOwner, // tslint:disable-next-line:trailing-comma newERC721TokenIdsByOwner - ] = await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + ] = await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -568,7 +568,7 @@ describe.only('matchOrders', () => { feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(96), 16), // 96% feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(96), 16), // 96% }; - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft2, signedOrderRight, takerAddress, @@ -607,7 +607,7 @@ describe.only('matchOrders', () => { feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -643,7 +643,7 @@ describe.only('matchOrders', () => { feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -679,7 +679,7 @@ describe.only('matchOrders', () => { feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -715,7 +715,7 @@ describe.only('matchOrders', () => { feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -751,7 +751,7 @@ describe.only('matchOrders', () => { feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -786,7 +786,7 @@ describe.only('matchOrders', () => { feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -919,7 +919,7 @@ describe.only('matchOrders', () => { feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 50% }; - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, @@ -957,7 +957,7 @@ describe.only('matchOrders', () => { feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; - await matchOrderTester.matchOrdersAndVerifyBalancesAsync( + await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, signedOrderRight, takerAddress, diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index ee298097d..7f2110e0f 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -35,7 +35,7 @@ export class MatchOrderTester { /// @param transactionReceipt Transaction receipt and logs produced by Exchange.matchOrders. /// @param takerAddress Address of taker (account that called Exchange.matchOrders) /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching. - private static async _verifyLogsAsync( + private static async _assertLogsAsync( signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder, transactionReceipt: TransactionReceiptWithDecodedLogs, @@ -66,7 +66,7 @@ export class MatchOrderTester { const feePaidByTakerRight = new BigNumber(rightLog.takerFeePaid); // Derive amount received by taker const amountReceivedByTaker = amountSoldByLeftMaker.sub(amountBoughtByRightMaker); - // Verify log values - left order + // Assert log values - left order expect( expectedTransferAmounts.amountBoughtByLeftMaker, 'Checking logged amount bought by left maker', @@ -83,7 +83,7 @@ export class MatchOrderTester { expectedTransferAmounts.feePaidByTakerLeft, 'Checking logged fee paid on left order by taker', ).to.be.bignumber.equal(feePaidByTakerLeft); - // Verify log values - right order + // Assert log values - right order expect( expectedTransferAmounts.amountBoughtByRightMaker, 'Checking logged amount bought by right maker', @@ -100,18 +100,18 @@ export class MatchOrderTester { expectedTransferAmounts.feePaidByTakerRight, 'Checking logged fee paid on right order by taker', ).to.be.bignumber.equal(feePaidByTakerRight); - // Verify derived amount received by taker + // Assert derived amount received by taker expect( expectedTransferAmounts.amountReceivedByTaker, 'Checking logged amount received by taker', ).to.be.bignumber.equal(amountReceivedByTaker); } - /// @dev Verifies all expected ERC20 and ERC721 account holdings match the real holdings. + /// @dev Asserts all expected ERC20 and ERC721 account holdings match the real holdings. /// @param expectedERC20BalancesByOwner Expected ERC20 balances. /// @param realERC20BalancesByOwner Real ERC20 balances. /// @param expectedERC721TokenIdsByOwner Expected ERC721 token owners. /// @param realERC721TokenIdsByOwner Real ERC20 token owners. - private static async _verifyAllKnownBalancesAsync( + private static async _assertAllKnownBalancesAsync( expectedERC20BalancesByOwner: ERC20BalancesByOwner, realERC20BalancesByOwner: ERC20BalancesByOwner, expectedERC721TokenIdsByOwner: ERC721TokenIdsByOwner, @@ -153,8 +153,7 @@ export class MatchOrderTester { this._erc721Wrapper = erc721Wrapper; this._feeTokenAddress = feeTokenAddress; } - /// @dev Matches two complementary orders and verifies results. - /// Validation either succeeds or throws. + /// @dev Matches two complementary orders and asserts results. /// @param signedOrderLeft First matched order. /// @param signedOrderRight Second matched order. /// @param takerAddress Address of taker (the address who matched the two orders) @@ -164,7 +163,7 @@ export class MatchOrderTester { /// @param initialLeftOrderFilledAmount How much left order has been filled, prior to matching orders. /// @param initialRightOrderFilledAmount How much the right order has been filled, prior to matching orders. /// @return New ERC20 balances & ERC721 token owners. - public async matchOrdersAndVerifyBalancesAsync( + public async matchOrdersAndAssertEffectsAsync( signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder, takerAddress: string, @@ -181,8 +180,8 @@ export class MatchOrderTester { if (initialRightOrderFilledAmount === undefined) { initialRightOrderFilledAmount = new BigNumber(0); } - // Verify initial order states - await this._verifyInitialOrderStatesAsync( + // Assert initial order states + await this._assertInitialOrderStatesAsync( signedOrderLeft, signedOrderRight, initialLeftOrderFilledAmount, @@ -196,24 +195,24 @@ export class MatchOrderTester { ); const newERC20BalancesByOwner = await this._erc20Wrapper.getBalancesAsync(); const newERC721TokenIdsByOwner = await this._erc721Wrapper.getBalancesAsync(); - // Verify logs - await MatchOrderTester._verifyLogsAsync( + // Assert logs + await MatchOrderTester._assertLogsAsync( signedOrderLeft, signedOrderRight, transactionReceipt, takerAddress, expectedTransferAmounts, ); - // Verify exchange state - await this._verifyExchangeStateAsync( + // Assert exchange state + await this._assertExchangeStateAsync( signedOrderLeft, signedOrderRight, initialLeftOrderFilledAmount, initialRightOrderFilledAmount, expectedTransferAmounts, ); - // Verify balances of makers, taker, and fee recipients - await this._verifyBalancesAsync( + // Assert balances of makers, taker, and fee recipients + await this._assertBalancesAsync( signedOrderLeft, signedOrderRight, erc20BalancesByOwner, @@ -225,25 +224,25 @@ export class MatchOrderTester { ); return [newERC20BalancesByOwner, newERC721TokenIdsByOwner]; } - /// @dev Verifies initial exchange state for the left and right orders. + /// @dev Asserts initial exchange state for the left and right orders. /// @param signedOrderLeft First matched order. /// @param signedOrderRight Second matched order. /// @param expectedOrderFilledAmountLeft How much left order has been filled, prior to matching orders. /// @param expectedOrderFilledAmountRight How much the right order has been filled, prior to matching orders. - private async _verifyInitialOrderStatesAsync( + private async _assertInitialOrderStatesAsync( signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder, expectedOrderFilledAmountLeft: BigNumber, expectedOrderFilledAmountRight: BigNumber, ): Promise { - // Verify left order initial state + // Assert left order initial state const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( orderHashUtils.getOrderHashHex(signedOrderLeft), ); expect(expectedOrderFilledAmountLeft, 'Checking inital state of left order').to.be.bignumber.equal( orderTakerAssetFilledAmountLeft, ); - // Verify right order initial state + // Assert right order initial state const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( orderHashUtils.getOrderHashHex(signedOrderRight), ); @@ -251,20 +250,20 @@ export class MatchOrderTester { orderTakerAssetFilledAmountRight, ); } - /// @dev Verifies the exchange state against the expected amounts transferred by from matching orders. + /// @dev Asserts the exchange state against the expected amounts transferred by from matching orders. /// @param signedOrderLeft First matched order. /// @param signedOrderRight Second matched order. /// @param initialLeftOrderFilledAmount How much left order has been filled, prior to matching orders. /// @param initialRightOrderFilledAmount How much the right order has been filled, prior to matching orders. /// @return TransferAmounts A struct containing the expected transfer amounts. - private async _verifyExchangeStateAsync( + private async _assertExchangeStateAsync( signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder, initialLeftOrderFilledAmount: BigNumber, initialRightOrderFilledAmount: BigNumber, expectedTransferAmounts: TransferAmounts, ): Promise { - // Verify state for left order: amount bought by left maker + // Assert state for left order: amount bought by left maker let amountBoughtByLeftMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( orderHashUtils.getOrderHashHex(signedOrderLeft), ); @@ -273,7 +272,7 @@ export class MatchOrderTester { expectedTransferAmounts.amountBoughtByLeftMaker, 'Checking exchange state for left order', ).to.be.bignumber.equal(amountBoughtByLeftMaker); - // Verify state for right order: amount bought by right maker + // Assert state for right order: amount bought by right maker let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( orderHashUtils.getOrderHashHex(signedOrderRight), ); @@ -282,7 +281,7 @@ export class MatchOrderTester { expectedTransferAmounts.amountBoughtByRightMaker, 'Checking exchange state for right order', ).to.be.bignumber.equal(amountBoughtByRightMaker); - // Verify left order status + // Assert left order status const maxAmountBoughtByLeftMaker = signedOrderLeft.takerAssetAmount.minus(initialLeftOrderFilledAmount); const leftOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderLeft); const leftExpectedStatus = expectedTransferAmounts.amountBoughtByLeftMaker.equals(maxAmountBoughtByLeftMaker) @@ -291,7 +290,7 @@ export class MatchOrderTester { expect(leftOrderInfo.orderStatus as OrderStatus, 'Checking exchange status for left order').to.be.equal( leftExpectedStatus, ); - // Verify right order status + // Assert right order status const maxAmountBoughtByRightMaker = signedOrderRight.takerAssetAmount.minus(initialRightOrderFilledAmount); const rightOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderRight); const rightExpectedStatus = expectedTransferAmounts.amountBoughtByRightMaker.equals(maxAmountBoughtByRightMaker) @@ -301,7 +300,7 @@ export class MatchOrderTester { rightExpectedStatus, ); } - /// @dev Verifies account balances after matching orders. + /// @dev Asserts account balances after matching orders. /// @param signedOrderLeft First matched order. /// @param signedOrderRight Second matched order. /// @param initialERC20BalancesByOwner ERC20 balances prior to order matching. @@ -310,7 +309,7 @@ export class MatchOrderTester { /// @param finalERC721TokenIdsByOwner ERC721 token owners after order matching. /// @param expectedTransferAmounts Expected amounts transferred as a result of order matching. /// @param takerAddress Address of taker (account that called Exchange.matchOrders). - private async _verifyBalancesAsync( + private async _assertBalancesAsync( signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder, initialERC20BalancesByOwner: ERC20BalancesByOwner, @@ -330,8 +329,8 @@ export class MatchOrderTester { initialERC721TokenIdsByOwner, expectedTransferAmounts, ); - // Verify balances of makers, taker, and fee recipients - await this._verifyMakerTakerAndFeeRecipientBalancesAsync( + // Assert balances of makers, taker, and fee recipients + await this._assertMakerTakerAndFeeRecipientBalancesAsync( signedOrderLeft, signedOrderRight, expectedERC20BalancesByOwner, @@ -340,8 +339,8 @@ export class MatchOrderTester { finalERC721TokenIdsByOwner, takerAddress, ); - // Verify balances for all known accounts - await MatchOrderTester._verifyAllKnownBalancesAsync( + // Assert balances for all known accounts + await MatchOrderTester._assertAllKnownBalancesAsync( expectedERC20BalancesByOwner, finalERC20BalancesByOwner, expectedERC721TokenIdsByOwner, @@ -465,7 +464,7 @@ export class MatchOrderTester { return [expectedNewERC20BalancesByOwner, expectedNewERC721TokenIdsByOwner]; } - /// @dev Verifies ERC20 account balances and ERC721 token holdings that result from order matching. + /// @dev Asserts ERC20 account balances and ERC721 token holdings that result from order matching. /// Specifically checks balances of makers, taker and fee recipients. /// @param signedOrderLeft First matched order. /// @param signedOrderRight Second matched order. @@ -474,7 +473,7 @@ export class MatchOrderTester { /// @param expectedERC721TokenIdsByOwner Expected ERC721 token owners. /// @param realERC721TokenIdsByOwner Real ERC20 token owners. /// @param takerAddress Address of taker (account that called Exchange.matchOrders). - private async _verifyMakerTakerAndFeeRecipientBalancesAsync( + private async _assertMakerTakerAndFeeRecipientBalancesAsync( signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder, expectedERC20BalancesByOwner: ERC20BalancesByOwner, -- cgit From ba15fb6a06b3c924ec3662a706c99f143fcef0f9 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 23 Aug 2018 13:27:59 -0700 Subject: Swapped direction of `expect` values to match output in failure cases --- .../contracts/test/utils/match_order_tester.ts | 63 +++++++++------------- 1 file changed, 26 insertions(+), 37 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index 7f2110e0f..e6347a962 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -67,44 +67,35 @@ export class MatchOrderTester { // Derive amount received by taker const amountReceivedByTaker = amountSoldByLeftMaker.sub(amountBoughtByRightMaker); // Assert log values - left order - expect( + expect(amountBoughtByLeftMaker, 'Checking logged amount bought by left maker').to.be.bignumber.equal( expectedTransferAmounts.amountBoughtByLeftMaker, - 'Checking logged amount bought by left maker', - ).to.be.bignumber.equal(amountBoughtByLeftMaker); - expect( + ); + expect(amountSoldByLeftMaker, 'Checking logged amount sold by left maker').to.be.bignumber.equal( expectedTransferAmounts.amountSoldByLeftMaker, - 'Checking logged amount sold by left maker', - ).to.be.bignumber.equal(amountSoldByLeftMaker); - expect( + ); + expect(feePaidByLeftMaker, 'Checking logged fee paid by left maker').to.be.bignumber.equal( expectedTransferAmounts.feePaidByLeftMaker, - 'Checking logged fee paid by left maker', - ).to.be.bignumber.equal(feePaidByLeftMaker); - expect( + ); + expect(feePaidByTakerLeft, 'Checking logged fee paid on left order by taker').to.be.bignumber.equal( expectedTransferAmounts.feePaidByTakerLeft, - 'Checking logged fee paid on left order by taker', - ).to.be.bignumber.equal(feePaidByTakerLeft); + ); // Assert log values - right order - expect( + expect(amountBoughtByRightMaker, 'Checking logged amount bought by right maker').to.be.bignumber.equal( expectedTransferAmounts.amountBoughtByRightMaker, - 'Checking logged amount bought by right maker', - ).to.be.bignumber.equal(amountBoughtByRightMaker); - expect( + ); + expect(amountSoldByRightMaker, 'Checking logged amount sold by right maker').to.be.bignumber.equal( expectedTransferAmounts.amountSoldByRightMaker, - 'Checking logged amount sold by right maker', - ).to.be.bignumber.equal(amountSoldByRightMaker); - expect( + ); + expect(feePaidByRightMaker, 'Checking logged fee paid by right maker').to.be.bignumber.equal( expectedTransferAmounts.feePaidByRightMaker, - 'Checking logged fee paid by right maker', - ).to.be.bignumber.equal(feePaidByRightMaker); - expect( + ); + expect(feePaidByTakerRight, 'Checking logged fee paid on right order by taker').to.be.bignumber.equal( expectedTransferAmounts.feePaidByTakerRight, - 'Checking logged fee paid on right order by taker', - ).to.be.bignumber.equal(feePaidByTakerRight); + ); // Assert derived amount received by taker - expect( + expect(amountReceivedByTaker, 'Checking logged amount received by taker').to.be.bignumber.equal( expectedTransferAmounts.amountReceivedByTaker, - 'Checking logged amount received by taker', - ).to.be.bignumber.equal(amountReceivedByTaker); + ); } /// @dev Asserts all expected ERC20 and ERC721 account holdings match the real holdings. /// @param expectedERC20BalancesByOwner Expected ERC20 balances. @@ -239,15 +230,15 @@ export class MatchOrderTester { const orderTakerAssetFilledAmountLeft = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( orderHashUtils.getOrderHashHex(signedOrderLeft), ); - expect(expectedOrderFilledAmountLeft, 'Checking inital state of left order').to.be.bignumber.equal( - orderTakerAssetFilledAmountLeft, + expect(orderTakerAssetFilledAmountLeft, 'Checking inital state of left order').to.be.bignumber.equal( + expectedOrderFilledAmountLeft, ); // Assert right order initial state const orderTakerAssetFilledAmountRight = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( orderHashUtils.getOrderHashHex(signedOrderRight), ); - expect(expectedOrderFilledAmountRight, 'Checking inital state of right order').to.be.bignumber.equal( - orderTakerAssetFilledAmountRight, + expect(orderTakerAssetFilledAmountRight, 'Checking inital state of right order').to.be.bignumber.equal( + expectedOrderFilledAmountRight, ); } /// @dev Asserts the exchange state against the expected amounts transferred by from matching orders. @@ -268,19 +259,17 @@ export class MatchOrderTester { orderHashUtils.getOrderHashHex(signedOrderLeft), ); amountBoughtByLeftMaker = amountBoughtByLeftMaker.minus(initialLeftOrderFilledAmount); - expect( + expect(amountBoughtByLeftMaker, 'Checking exchange state for left order').to.be.bignumber.equal( expectedTransferAmounts.amountBoughtByLeftMaker, - 'Checking exchange state for left order', - ).to.be.bignumber.equal(amountBoughtByLeftMaker); + ); // Assert state for right order: amount bought by right maker let amountBoughtByRightMaker = await this._exchangeWrapper.getTakerAssetFilledAmountAsync( orderHashUtils.getOrderHashHex(signedOrderRight), ); amountBoughtByRightMaker = amountBoughtByRightMaker.minus(initialRightOrderFilledAmount); - expect( + expect(amountBoughtByRightMaker, 'Checking exchange state for right order').to.be.bignumber.equal( expectedTransferAmounts.amountBoughtByRightMaker, - 'Checking exchange state for right order', - ).to.be.bignumber.equal(amountBoughtByRightMaker); + ); // Assert left order status const maxAmountBoughtByLeftMaker = signedOrderLeft.takerAssetAmount.minus(initialLeftOrderFilledAmount); const leftOrderInfo: OrderInfo = await this._exchangeWrapper.getOrderInfoAsync(signedOrderLeft); -- cgit From f8e565bc06b48f4e59b9a246fdf1e4b16245bd83 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 23 Aug 2018 16:07:13 -0700 Subject: Tests for matchOrders edge cases --- packages/contracts/test/exchange/match_orders.ts | 84 ++++++++++++++++++++---- 1 file changed, 71 insertions(+), 13 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index 95d4b7502..95a6fff33 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -173,8 +173,7 @@ describe.only('matchOrders', () => { erc721TokenIdsByOwner = await erc721Wrapper.getBalancesAsync(); }); - /* - it.only('should transfer the correct amounts when orders completely fill each other', async () => { + it('Should give right maker a better price when correct price is not integral', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ makerAddress: makerAddressLeft, @@ -190,6 +189,67 @@ describe.only('matchOrders', () => { takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(3000), 0), feeRecipientAddress: feeRecipientAddressRight, }); + // Note: + // The maker/taker fee percentage paid on the right order differs because + // they received different sale prices. + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2000), 0), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1001), 0), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1001), 0), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(300), 0), // @TODO: Update once rounding up is implemented + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10.01), 16), // @TODO: Update once rounding up is implemented + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1700), 0), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 16), // @TODO: Update once rounding up is implemented + }; + // Match signedOrderLeft with signedOrderRight + await matchOrderTester.matchOrdersAndAssertEffectsAsync( + signedOrderLeft, + signedOrderRight, + takerAddress, + erc20BalancesByOwner, + erc721TokenIdsByOwner, + expectedTransferAmounts, + ); + }); + + it('Should give left maker a better price when correct price is non-integral', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAddress: makerAddressLeft, + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(12), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(97), 0), + feeRecipientAddress: feeRecipientAddressLeft, + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAddress: makerAddressRight, + makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(89), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), + feeRecipientAddress: feeRecipientAddressRight, + }); + // Note: + // The maker/taker fee percentage paid on the left order differs because + // they received different sale prices. + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(11), 0), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(89), 0), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber('91.6666666666666666'), 16), // 91.6% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(89), 0), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 0), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber('91.7525773195876288'), 16), // 91.75% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + }; // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndAssertEffectsAsync( signedOrderLeft, @@ -197,17 +257,11 @@ describe.only('matchOrders', () => { takerAddress, erc20BalancesByOwner, erc721TokenIdsByOwner, + expectedTransferAmounts, ); - // // Verify left order was fully filled - // const leftOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderLeft); - // expect(leftOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); - // Verify right order was fully filled - // const rightOrderInfo: OrderInfo = await exchangeWrapper.getOrderInfoAsync(signedOrderRight); - // expect(rightOrderInfo.orderStatus as OrderStatus).to.be.equal(OrderStatus.FULLY_FILLED); }); - */ - it('Jacobs Example', async () => { + it('Should transfer correct amounts when right order fill amount deviates from amount derived by `Exchange.fillOrder`', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ makerAddress: makerAddressLeft, @@ -230,13 +284,17 @@ describe.only('matchOrders', () => { amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Right Maker + // Note: + // For order [4,2] valid fill amounts through `Exchange.fillOrder` would be [2, 1] or [4, 2] + // In this case we have fill amounts of [3, 1] which is attainable through + // `Exchange.matchOrders` but not `Exchange.fillOrder` amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 16), // 75% + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), // @TODO: Update once rounding up is implemented + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 16), // 75% (@TODO: Update once rounding up is implemented) // Taker amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(9), 0), feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 16), // 50% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 16), // 50% (@TODO: Update once rounding up is implemented) }; // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndAssertEffectsAsync( -- cgit From 81dc893d1d07e5a8485fb40aa247428cda79d788 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 23 Aug 2018 16:07:39 -0700 Subject: Removed a redundant comment from matchOrders --- packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index b3f376eb8..d932c743f 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -165,11 +165,6 @@ contract MixinMatchOrders is pure returns (LibFillResults.MatchedFillResults memory matchedFillResults) { - // We settle orders at the exchange rate of the right order. - // The amount saved by the left maker goes to the taker. - // Either the left or right order will be fully filled; possibly both. - // The left order is fully filled iff the right order can sell more than left can buy. - // Derive maker asset amounts for left & right orders, given store taker assert amounts uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount); uint256 leftMakerAssetAmountRemaining = getPartialAmountFloor( @@ -184,6 +179,7 @@ contract MixinMatchOrders is rightTakerAssetAmountRemaining ); + // Calculate fill results for maker and taker assets if (leftTakerAssetAmountRemaining >= rightMakerAssetAmountRemaining) { // Case 1: Right order is fully filled: maximally fill right matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining; -- cgit From 6833e243b7b3aa6991f286e07666c6229313a046 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 23 Aug 2018 16:14:51 -0700 Subject: Addressed linter errors in match order tesster --- packages/contracts/test/utils/match_order_tester.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index e6347a962..4d27fc630 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -161,16 +161,9 @@ export class MatchOrderTester { erc20BalancesByOwner: ERC20BalancesByOwner, erc721TokenIdsByOwner: ERC721TokenIdsByOwner, expectedTransferAmounts: TransferAmounts, - initialLeftOrderFilledAmount?: BigNumber, - initialRightOrderFilledAmount?: BigNumber, + initialLeftOrderFilledAmount: BigNumber = new BigNumber(0), + initialRightOrderFilledAmount: BigNumber = new BigNumber(0), ): Promise<[ERC20BalancesByOwner, ERC721TokenIdsByOwner]> { - // Assign default values to optional parameters if undefined - if (initialLeftOrderFilledAmount === undefined) { - initialLeftOrderFilledAmount = new BigNumber(0); - } - if (initialRightOrderFilledAmount === undefined) { - initialRightOrderFilledAmount = new BigNumber(0); - } // Assert initial order states await this._assertInitialOrderStatesAsync( signedOrderLeft, -- cgit From a93f95c55ea43bc89d0633190590865d9723b2f9 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 23 Aug 2018 16:16:01 -0700 Subject: Wording in MixinMatchOrders --- packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index d932c743f..a0696c616 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -181,7 +181,7 @@ contract MixinMatchOrders is // Calculate fill results for maker and taker assets if (leftTakerAssetAmountRemaining >= rightMakerAssetAmountRemaining) { - // Case 1: Right order is fully filled: maximally fill right + // Case 1: Right order is fully filled matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining; matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining; matchedFillResults.left.makerAssetFilledAmount = getPartialAmountFloor( @@ -191,7 +191,7 @@ contract MixinMatchOrders is ); matchedFillResults.left.takerAssetFilledAmount = matchedFillResults.right.makerAssetFilledAmount; } else { - // Case 2: Left order is fully filled: maximall fill left + // Case 2: Left order is fully filled matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining; matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining; matchedFillResults.right.makerAssetFilledAmount = matchedFillResults.left.takerAssetFilledAmount; -- cgit From 0a6f107243eb7df309766cc83942e667ce28e858 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 23 Aug 2018 16:28:03 -0700 Subject: Added temporary @todo to MixinMatchOrders --- .../contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index a0696c616..8f289ee85 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -209,24 +209,24 @@ contract MixinMatchOrders is ); // Compute fees for left order - matchedFillResults.left.makerFeePaid = getPartialAmount( + matchedFillResults.left.makerFeePaid = getPartialAmountFloor( matchedFillResults.left.makerAssetFilledAmount, leftOrder.makerAssetAmount, leftOrder.makerFee ); - matchedFillResults.left.takerFeePaid = getPartialAmount( + matchedFillResults.left.takerFeePaid = getPartialAmountFloor( matchedFillResults.left.takerAssetFilledAmount, leftOrder.takerAssetAmount, leftOrder.takerFee ); // Compute fees for right order - matchedFillResults.right.makerFeePaid = getPartialAmount( + matchedFillResults.right.makerFeePaid = getPartialAmountFloor( matchedFillResults.right.makerAssetFilledAmount, rightOrder.makerAssetAmount, rightOrder.makerFee ); - matchedFillResults.right.takerFeePaid = getPartialAmount( + matchedFillResults.right.takerFeePaid = getPartialAmountFloor( matchedFillResults.right.takerAssetFilledAmount, rightOrder.takerAssetAmount, rightOrder.takerFee -- cgit From 1c7ba6a31533b3202be7a464452b14aa58a0337b Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 24 Aug 2018 17:17:52 -0700 Subject: Extract only `fill` event logs --- packages/contracts/test/utils/match_order_tester.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/utils/match_order_tester.ts b/packages/contracts/test/utils/match_order_tester.ts index 4d27fc630..e0c55b834 100644 --- a/packages/contracts/test/utils/match_order_tester.ts +++ b/packages/contracts/test/utils/match_order_tester.ts @@ -42,10 +42,11 @@ export class MatchOrderTester { takerAddress: string, expectedTransferAmounts: TransferAmounts, ): Promise { - // Should have two logs -- one for each order. - expect(transactionReceipt.logs.length, 'Checking number of logs').to.be.equal(2); + // Should have two fill event logs -- one for each order. + const transactionFillLogs = _.filter(transactionReceipt.logs, ['event', 'Fill']); + expect(transactionFillLogs.length, 'Checking number of logs').to.be.equal(2); // First log is for left fill - const leftLog = (transactionReceipt.logs[0] as any).args as LoggedTransferAmounts; + const leftLog = (transactionFillLogs[0] as any).args as LoggedTransferAmounts; expect(leftLog.makerAddress, 'Checking logged maker address of left order').to.be.equal( signedOrderLeft.makerAddress, ); @@ -55,7 +56,7 @@ export class MatchOrderTester { const feePaidByLeftMaker = new BigNumber(leftLog.makerFeePaid); const feePaidByTakerLeft = new BigNumber(leftLog.takerFeePaid); // Second log is for right fill - const rightLog = (transactionReceipt.logs[1] as any).args as LoggedTransferAmounts; + const rightLog = (transactionFillLogs[1] as any).args as LoggedTransferAmounts; expect(rightLog.makerAddress, 'Checking logged maker address of right order').to.be.equal( signedOrderRight.makerAddress, ); -- cgit From c7a7ae7e18bb6d0f5f148a37b63dccf2e485cc6f Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 24 Aug 2018 17:19:28 -0700 Subject: Give right maker better price when correct value is not integral --- packages/contracts/test/exchange/match_orders.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index 95a6fff33..2e6e9410a 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -191,7 +191,8 @@ describe.only('matchOrders', () => { }); // Note: // The maker/taker fee percentage paid on the right order differs because - // they received different sale prices. + // they received different sale prices. Similarly, the right maker pays a + // slightly higher lower than the right taker. const expectedTransferAmounts = { // Left Maker amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2000), 0), @@ -199,12 +200,12 @@ describe.only('matchOrders', () => { feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Right Maker amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1001), 0), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(300), 0), // @TODO: Update once rounding up is implemented - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10.01), 16), // @TODO: Update once rounding up is implemented + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(301), 0), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10.01), 16), // 10.01% // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1700), 0), + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1699), 0), feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 16), // @TODO: Update once rounding up is implemented + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber('10.0333333333333333'), 16), // 10.03% }; // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndAssertEffectsAsync( @@ -217,7 +218,7 @@ describe.only('matchOrders', () => { ); }); - it('Should give left maker a better price when correct price is non-integral', async () => { + it('Should give left maker a better price when correct price is not integral', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ makerAddress: makerAddressLeft, @@ -288,13 +289,16 @@ describe.only('matchOrders', () => { // For order [4,2] valid fill amounts through `Exchange.fillOrder` would be [2, 1] or [4, 2] // In this case we have fill amounts of [3, 1] which is attainable through // `Exchange.matchOrders` but not `Exchange.fillOrder` + // Note: + // The right maker fee differs from the right taker fee because their exchange rate differs. + // The right maker always receives the better exchange and fee price. amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 0), // @TODO: Update once rounding up is implemented - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 16), // 75% (@TODO: Update once rounding up is implemented) + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 0), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 16), // 75% // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(9), 0), + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(8), 0), feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), 16), // 50% (@TODO: Update once rounding up is implemented) + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndAssertEffectsAsync( -- cgit From 287830d6e01a919bcb4ac4ccec4173cdbdcf9470 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 24 Aug 2018 17:29:31 -0700 Subject: Run all tests --- packages/contracts/test/exchange/match_orders.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index 2e6e9410a..8732e20ba 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -24,7 +24,7 @@ import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -describe.only('matchOrders', () => { +describe('matchOrders', () => { let makerAddressLeft: string; let makerAddressRight: string; let owner: string; -- cgit From ec2e726be0a653465d3c83e640aa4c1556a0f10f Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 24 Aug 2018 18:09:32 -0700 Subject: Rephrased some of the math in MixinMatchOrders to improve readability --- packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index 8f289ee85..226a1bfb6 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -184,12 +184,12 @@ contract MixinMatchOrders is // Case 1: Right order is fully filled matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining; matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining; + matchedFillResults.left.takerAssetFilledAmount = matchedFillResults.right.makerAssetFilledAmount; matchedFillResults.left.makerAssetFilledAmount = getPartialAmountFloor( leftOrder.makerAssetAmount, leftOrder.takerAssetAmount, - matchedFillResults.right.makerAssetFilledAmount + matchedFillResults.left.takerAssetFilledAmount ); - matchedFillResults.left.takerAssetFilledAmount = matchedFillResults.right.makerAssetFilledAmount; } else { // Case 2: Left order is fully filled matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining; @@ -198,7 +198,7 @@ contract MixinMatchOrders is matchedFillResults.right.takerAssetFilledAmount = getPartialAmountCeil( rightOrder.takerAssetAmount, rightOrder.makerAssetAmount, - matchedFillResults.left.takerAssetFilledAmount + matchedFillResults.right.makerAssetFilledAmount ); } -- cgit From 878db3b84993d7c9724c0ed8591493a4e2b6cc94 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 24 Aug 2018 18:48:29 -0700 Subject: Added comments to order matching --- .../src/2.0.0/protocol/Exchange/MixinMatchOrders.sol | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index 226a1bfb6..4b8360c23 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -179,12 +179,22 @@ contract MixinMatchOrders is rightTakerAssetAmountRemaining ); - // Calculate fill results for maker and taker assets + // Calculate fill results for maker and taker assets: at least one order will be fully filled. + // The maximum amount the left maker can buy is `leftTakerAssetAmountRemaining` + // The maximum amount the right maker can sell is `rightMakerAssetAmountRemaining` + // We have two distinct cases for calculating the fill results: + // Case 1. + // If the left maker can buy more than the right maker can sell, then only the right order is fully filled. + // If the left maker can buy exactly what the right maker can sell, then both orders are fully filled. + // Case 2. + // If the left maker cannot buy more than the right maker can sell, then only the left order is fully filled. if (leftTakerAssetAmountRemaining >= rightMakerAssetAmountRemaining) { // Case 1: Right order is fully filled matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining; matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining; matchedFillResults.left.takerAssetFilledAmount = matchedFillResults.right.makerAssetFilledAmount; + // Round down to ensure the maker's exchange rate does not exceed the price specified by the order. + // We favor the maker when the exchange rate must be rounded. matchedFillResults.left.makerAssetFilledAmount = getPartialAmountFloor( leftOrder.makerAssetAmount, leftOrder.takerAssetAmount, @@ -195,6 +205,8 @@ contract MixinMatchOrders is matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining; matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining; matchedFillResults.right.makerAssetFilledAmount = matchedFillResults.left.takerAssetFilledAmount; + // Round up to ensure the maker's exchange rate does not exceed the price specified by the order. + // We favor the maker when the exchange rate must be rounded. matchedFillResults.right.takerAssetFilledAmount = getPartialAmountCeil( rightOrder.takerAssetAmount, rightOrder.makerAssetAmount, -- cgit From 3ac182ee914080e513b007758c001642b6d07e3f Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 26 Aug 2018 17:12:13 +0100 Subject: Fix file path to main and types in package.json --- packages/0x.js/CHANGELOG.json | 9 +++++++++ packages/0x.js/package.json | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json index 346938958..f242b5496 100644 --- a/packages/0x.js/CHANGELOG.json +++ b/packages/0x.js/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.0.1-rc.5", + "changes": [ + { + "note": + "Fix `main` and `types` package.json entries so that they point to the new location of index.d.ts and index.js" + } + ] + }, { "version": "1.0.1-rc.4", "changes": [ diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index b5cb6c7e4..484342422 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -12,8 +12,8 @@ "tokens", "exchange" ], - "main": "lib/src/index.js", - "types": "lib/src/index.d.ts", + "main": "lib/index.js", + "types": "lib/index.d.ts", "scripts": { "watch_without_deps": "tsc -w", "build": "yarn build:all", -- cgit From 35ba3e6f7c118b0cd199fd062d3923aa215acc6a Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 26 Aug 2018 17:35:17 +0100 Subject: Bumop 0x.js version --- packages/0x.js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 484342422..5d4a6ed0e 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -1,6 +1,6 @@ { "name": "0x.js", - "version": "1.0.1-rc.4", + "version": "1.0.1-rc.5", "engines": { "node": ">=6.12" }, -- cgit From c4dadf4bfd5b955221d5e8cd5e702720efd7873b Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 26 Aug 2018 17:49:06 +0100 Subject: Combine imports --- .../src/contract_wrappers/contract_wrapper.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts index 490a6c50a..ba36afea1 100644 --- a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts @@ -1,7 +1,14 @@ import { AbiDecoder, intervalUtils, logUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; -import { ContractArtifact } from 'ethereum-types'; -import { BlockParamLiteral, ContractAbi, FilterObject, LogEntry, LogWithDecodedArgs, RawLog } from 'ethereum-types'; +import { + BlockParamLiteral, + ContractAbi, + ContractArtifact, + FilterObject, + LogEntry, + LogWithDecodedArgs, + RawLog, +} from 'ethereum-types'; import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream'; import * as _ from 'lodash'; -- cgit From 09d64961359cb63ff0bf1496b5f30840f030d8db Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 26 Aug 2018 20:53:21 +0100 Subject: Change exit code to failure --- packages/monorepo-scripts/src/test_installation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/monorepo-scripts/src/test_installation.ts b/packages/monorepo-scripts/src/test_installation.ts index 87c4ad1d7..e626fb891 100644 --- a/packages/monorepo-scripts/src/test_installation.ts +++ b/packages/monorepo-scripts/src/test_installation.ts @@ -85,7 +85,7 @@ function logIfDefined(x: any): void { logIfDefined(packageError.error.stdout); logIfDefined(packageError.error.stack); }); - process.exit(0); + process.exit(1); } })().catch(err => { utils.log(`Unexpected error: ${err.message}`); -- cgit From 40cf805e5ec0b49b6b193bad941b97049864ba0b Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 26 Aug 2018 20:54:15 +0100 Subject: Also use failure exit code if unexpected error occurs --- packages/monorepo-scripts/src/test_installation.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/monorepo-scripts/src/test_installation.ts b/packages/monorepo-scripts/src/test_installation.ts index e626fb891..10709af25 100644 --- a/packages/monorepo-scripts/src/test_installation.ts +++ b/packages/monorepo-scripts/src/test_installation.ts @@ -86,10 +86,12 @@ function logIfDefined(x: any): void { logIfDefined(packageError.error.stack); }); process.exit(1); + } else { + process.exit(0); } })().catch(err => { utils.log(`Unexpected error: ${err.message}`); - process.exit(0); + process.exit(1); }); async function testInstallPackageAsync( -- cgit From 52fde551e47c774c115c6b6fae085025dc742196 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 26 Aug 2018 21:16:32 +0100 Subject: Remove check since this method is now used in multiple places --- packages/monorepo-scripts/src/utils/changelog_utils.ts | 5 ----- 1 file changed, 5 deletions(-) (limited to 'packages') diff --git a/packages/monorepo-scripts/src/utils/changelog_utils.ts b/packages/monorepo-scripts/src/utils/changelog_utils.ts index 4781b3b7d..8058d222b 100644 --- a/packages/monorepo-scripts/src/utils/changelog_utils.ts +++ b/packages/monorepo-scripts/src/utils/changelog_utils.ts @@ -19,11 +19,6 @@ CHANGELOG export const changelogUtils = { getChangelogMdTitle(versionChangelog: VersionChangelog): string { - if (_.isUndefined(versionChangelog.timestamp)) { - throw new Error( - 'All CHANGELOG.json entries must be updated to include a timestamp before generating their MD version', - ); - } const date = moment(`${versionChangelog.timestamp}`, 'X').format('MMMM D, YYYY'); const title = `\n## v${versionChangelog.version} - _${date}_\n\n`; return title; -- cgit From db54588d055cc2cc6b7f1a99c7438f6fd2053765 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 26 Aug 2018 22:00:51 +0100 Subject: Add BlockParamLiteral to template --- packages/contract_templates/contract.handlebars | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contract_templates/contract.handlebars b/packages/contract_templates/contract.handlebars index 5c36a765d..466893aa7 100644 --- a/packages/contract_templates/contract.handlebars +++ b/packages/contract_templates/contract.handlebars @@ -2,7 +2,7 @@ // tslint:disable:no-unused-variable // tslint:disable:no-unbound-method import { BaseContract } from '@0xproject/base-contract'; -import { BlockParam, CallData, ContractAbi, ContractArtifact, DecodedLogArgs, MethodAbi, Provider, TxData, TxDataPayable } from 'ethereum-types'; +import { BlockParam, BlockParamLiteral, CallData, ContractAbi, ContractArtifact, DecodedLogArgs, MethodAbi, Provider, TxData, TxDataPayable } from 'ethereum-types'; import { BigNumber, classUtils, logUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as ethers from 'ethers'; -- cgit From 3a5c6ed00fb96488897fa95fbf267f71def24fc4 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 26 Aug 2018 23:05:04 +0100 Subject: Fix sra-spec `main` and `types` in package.json --- packages/sra-spec/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/sra-spec/package.json b/packages/sra-spec/package.json index 706f9f0aa..ccb7ccb71 100644 --- a/packages/sra-spec/package.json +++ b/packages/sra-spec/package.json @@ -5,8 +5,8 @@ "node": ">=6.12" }, "description": "Standard Relayer API Open API Spec", - "main": "lib/src/index.js", - "types": "lib/src/index.d.ts", + "main": "lib/index.js", + "types": "lib/index.d.ts", "scripts": { "serve": "redoc-cli serve lib/api.json --watch", "watch_without_deps": "run-p build-json:watch serve", -- cgit From 6a6b424c86b507d4dbfe5821ef81e7f76b818ef9 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 26 Aug 2018 23:35:47 +0100 Subject: Move md files to lib folder during build --- packages/sra-spec/package.json | 4 +++- packages/sra-spec/src/md/index.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/sra-spec/package.json b/packages/sra-spec/package.json index ccb7ccb71..9a192d46f 100644 --- a/packages/sra-spec/package.json +++ b/packages/sra-spec/package.json @@ -17,9 +17,10 @@ "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", "test:circleci": "yarn test:coverage", "clean": "shx rm -rf lib", - "build": "tsc && yarn build-json", + "build": "tsc && copy_md_files && yarn build-json", "build-json": "ts-node build_scripts/buildJson.ts", "build-json:watch": "chokidar 'src/**/*' -c 'yarn build-json' ", + "copy_md_files": "copyfiles -u 2 './src/md/**/*.md' ./lib/md", "deploy-site": "discharge deploy" }, "repository": { @@ -42,6 +43,7 @@ "@types/node": "^10.5.3", "chai": "^4.0.1", "chokidar-cli": "^1.2.0", + "copyfiles": "^2.0.0", "dirty-chai": "^2.0.1", "discharge": "^0.7.1", "mocha": "^4.0.1", diff --git a/packages/sra-spec/src/md/index.ts b/packages/sra-spec/src/md/index.ts index 076c3c45c..0d1147aa0 100644 --- a/packages/sra-spec/src/md/index.ts +++ b/packages/sra-spec/src/md/index.ts @@ -1,5 +1,5 @@ import { readFileSync } from 'fs'; export const md = { - introduction: readFileSync('src/md/introduction.md').toString(), + introduction: readFileSync('lib/md/introduction.md').toString(), }; -- cgit From 41559c39b9b4fac9c512309f3271796bb9fb0fb0 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 26 Aug 2018 23:43:19 +0100 Subject: Fix command --- packages/sra-spec/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/sra-spec/package.json b/packages/sra-spec/package.json index 9a192d46f..f0820e136 100644 --- a/packages/sra-spec/package.json +++ b/packages/sra-spec/package.json @@ -17,7 +17,7 @@ "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", "test:circleci": "yarn test:coverage", "clean": "shx rm -rf lib", - "build": "tsc && copy_md_files && yarn build-json", + "build": "tsc && yarn copy_md_files && yarn build-json", "build-json": "ts-node build_scripts/buildJson.ts", "build-json:watch": "chokidar 'src/**/*' -c 'yarn build-json' ", "copy_md_files": "copyfiles -u 2 './src/md/**/*.md' ./lib/md", -- cgit From 9fc8a6e214279cb156fa467fbada2b7d5368d616 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 27 Aug 2018 10:09:51 +0100 Subject: Try relative path --- packages/sra-spec/src/md/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/sra-spec/src/md/index.ts b/packages/sra-spec/src/md/index.ts index 0d1147aa0..076c3c45c 100644 --- a/packages/sra-spec/src/md/index.ts +++ b/packages/sra-spec/src/md/index.ts @@ -1,5 +1,5 @@ import { readFileSync } from 'fs'; export const md = { - introduction: readFileSync('lib/md/introduction.md').toString(), + introduction: readFileSync('src/md/introduction.md').toString(), }; -- cgit From 90c9e3496a6db59c39265d1974f766fed30c7877 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 27 Aug 2018 10:10:07 +0100 Subject: Actual relative path --- packages/sra-spec/src/md/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/sra-spec/src/md/index.ts b/packages/sra-spec/src/md/index.ts index 076c3c45c..d82e7061f 100644 --- a/packages/sra-spec/src/md/index.ts +++ b/packages/sra-spec/src/md/index.ts @@ -1,5 +1,5 @@ import { readFileSync } from 'fs'; export const md = { - introduction: readFileSync('src/md/introduction.md').toString(), + introduction: readFileSync('introduction.md').toString(), }; -- cgit From 4ac43a9fd2d3cc75b4f9aed92d85f722f28d34cb Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 27 Aug 2018 10:38:06 +0100 Subject: Try relative to root dir --- packages/sra-spec/src/md/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/sra-spec/src/md/index.ts b/packages/sra-spec/src/md/index.ts index d82e7061f..b32cba6f4 100644 --- a/packages/sra-spec/src/md/index.ts +++ b/packages/sra-spec/src/md/index.ts @@ -1,5 +1,7 @@ import { readFileSync } from 'fs'; +console.log('DIR', __dirname); + export const md = { - introduction: readFileSync('introduction.md').toString(), + introduction: readFileSync('lib/md/introduction.md').toString(), }; -- cgit From e4fc8a84145d14084c389f5c4e8307e84ebf0c41 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 27 Aug 2018 12:05:55 +0100 Subject: Use absolute path --- packages/sra-spec/src/md/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'packages') diff --git a/packages/sra-spec/src/md/index.ts b/packages/sra-spec/src/md/index.ts index b32cba6f4..4e778e073 100644 --- a/packages/sra-spec/src/md/index.ts +++ b/packages/sra-spec/src/md/index.ts @@ -1,7 +1,5 @@ import { readFileSync } from 'fs'; -console.log('DIR', __dirname); - export const md = { - introduction: readFileSync('lib/md/introduction.md').toString(), + introduction: readFileSync(`${__dirname}/introduction.md`).toString(), }; -- cgit From 6174d9ebb78b46d98a3e994ae00eea05a92bc84a Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 27 Aug 2018 12:34:34 +0100 Subject: Fix typo --- packages/monorepo-scripts/src/test_installation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/monorepo-scripts/src/test_installation.ts b/packages/monorepo-scripts/src/test_installation.ts index 10709af25..be39b1878 100644 --- a/packages/monorepo-scripts/src/test_installation.ts +++ b/packages/monorepo-scripts/src/test_installation.ts @@ -146,7 +146,7 @@ async function testInstallPackageAsync( const transpiledIndexFilePath = path.join(testDirectory, 'index.js'); utils.log(`Running test script with ${packageName} imported`); await execAsync(`node ${transpiledIndexFilePath}`); - utils.log(`Successfilly ran test script with ${packageName} imported`); + utils.log(`Successfully ran test script with ${packageName} imported`); } await rimrafAsync(testDirectory); } -- cgit From bc4149683e2dda2b36c29cc81fadf1ae4de7d8b1 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 27 Aug 2018 12:39:10 +0100 Subject: Skip doc generation for local publishes since we test this in a separate CI test --- packages/monorepo-scripts/src/publish.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'packages') diff --git a/packages/monorepo-scripts/src/publish.ts b/packages/monorepo-scripts/src/publish.ts index 7e2e64536..646818c58 100644 --- a/packages/monorepo-scripts/src/publish.ts +++ b/packages/monorepo-scripts/src/publish.ts @@ -74,9 +74,11 @@ async function confirmAsync(message: string): Promise { }); utils.log(`Calling 'lerna publish'...`); await lernaPublishAsync(packageToNextVersion); - const isStaging = false; - const shouldUploadDocs = !configs.IS_LOCAL_PUBLISH; - await generateAndUploadDocJsonsAsync(packagesWithDocs, isStaging, shouldUploadDocs); + if (!configs.IS_LOCAL_PUBLISH) { + const isStaging = false; + const shouldUploadDocs = true; + await generateAndUploadDocJsonsAsync(packagesWithDocs, isStaging, shouldUploadDocs); + } const isDryRun = configs.IS_LOCAL_PUBLISH; await publishReleaseNotesAsync(updatedPublicPackages, isDryRun); })().catch(err => { -- cgit From b0c4eb8333930e355685588152ce027b01f014f6 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 27 Aug 2018 13:16:31 +0100 Subject: Update changelog files for RC packages --- packages/0x.js/CHANGELOG.json | 9 +++++++++ packages/connect/CHANGELOG.json | 9 +++++++++ packages/contract-wrappers/CHANGELOG.json | 9 +++++++++ packages/fill-scenarios/CHANGELOG.json | 9 +++++++++ packages/forwarder-helper/CHANGELOG.json | 9 +++++++++ packages/json-schemas/CHANGELOG.json | 9 +++++++++ packages/order-utils/CHANGELOG.json | 9 +++++++++ packages/order-watcher/CHANGELOG.json | 9 +++++++++ packages/sra-spec/CHANGELOG.json | 12 ++++++++++++ 9 files changed, 84 insertions(+) (limited to 'packages') diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json index f242b5496..08075f70e 100644 --- a/packages/0x.js/CHANGELOG.json +++ b/packages/0x.js/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.0.1-rc.6", + "changes": [ + { + "note": + "Fix missing `BlockParamLiteral` type import issue" + } + ] + }, { "version": "1.0.1-rc.5", "changes": [ diff --git a/packages/connect/CHANGELOG.json b/packages/connect/CHANGELOG.json index 6cf679b34..89b1fb728 100644 --- a/packages/connect/CHANGELOG.json +++ b/packages/connect/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "2.0.0-rc.2", + "changes": [ + { + "note": + "Dependencies updated" + } + ] + }, { "version": "2.0.0-rc.1", "changes": [ diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index 3809ad098..02d28e68a 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.0.1-rc.5", + "changes": [ + { + "note": + "Fix missing `BlockParamLiteral` type import issue" + } + ] + }, { "version": "1.0.1-rc.4", "changes": [ diff --git a/packages/fill-scenarios/CHANGELOG.json b/packages/fill-scenarios/CHANGELOG.json index 481273fc4..be739873e 100644 --- a/packages/fill-scenarios/CHANGELOG.json +++ b/packages/fill-scenarios/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.0.1-rc.5", + "changes": [ + { + "note": + "Dependencies updated" + } + ] + }, { "version": "1.0.1-rc.4", "changes": [ diff --git a/packages/forwarder-helper/CHANGELOG.json b/packages/forwarder-helper/CHANGELOG.json index b99a98b93..6c7305146 100644 --- a/packages/forwarder-helper/CHANGELOG.json +++ b/packages/forwarder-helper/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.0.1-rc.2", + "changes": [ + { + "note": + "Dependencies updated" + } + ] + }, { "version": "1.0.1-rc.1", "changes": [ diff --git a/packages/json-schemas/CHANGELOG.json b/packages/json-schemas/CHANGELOG.json index b17267414..caaf88187 100644 --- a/packages/json-schemas/CHANGELOG.json +++ b/packages/json-schemas/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.0.1-rc.6", + "changes": [ + { + "note": + "Dependencies updated" + } + ] + }, { "version": "1.0.1-rc.5", "changes": [ diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index d18bf51ed..a25a6a82a 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.0.1-rc.6", + "changes": [ + { + "note": + "Fix missing `BlockParamLiteral` type import issue" + } + ] + }, { "version": "1.0.1-rc.5", "changes": [ diff --git a/packages/order-watcher/CHANGELOG.json b/packages/order-watcher/CHANGELOG.json index fe38e2175..5529bfa9e 100644 --- a/packages/order-watcher/CHANGELOG.json +++ b/packages/order-watcher/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.0.1-rc.5", + "changes": [ + { + "note": + "Fix missing `BlockParamLiteral` type import issue" + } + ] + }, { "version": "1.0.1-rc.4", "changes": [ diff --git a/packages/sra-spec/CHANGELOG.json b/packages/sra-spec/CHANGELOG.json index 8467f3c68..14830eb05 100644 --- a/packages/sra-spec/CHANGELOG.json +++ b/packages/sra-spec/CHANGELOG.json @@ -1,4 +1,16 @@ [ + { + "version": "1.0.1-rc.6", + "changes": [ + { + "note": + "Fix `main` and `types` package.json entries so that they point to the new location of index.d.ts and index.js" + }, + { + "note": "Fix relative path to introduction MD file" + } + ] + }, { "version": "1.0.1-rc.5", "changes": [ -- cgit From cd08a9c1218fa7c4819e31248e50da2a4f45ee36 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 27 Aug 2018 13:43:29 +0100 Subject: Fix prettier --- packages/0x.js/CHANGELOG.json | 3 +-- packages/connect/CHANGELOG.json | 3 +-- packages/contract-wrappers/CHANGELOG.json | 3 +-- packages/fill-scenarios/CHANGELOG.json | 3 +-- packages/forwarder-helper/CHANGELOG.json | 3 +-- packages/json-schemas/CHANGELOG.json | 3 +-- packages/order-utils/CHANGELOG.json | 3 +-- packages/order-watcher/CHANGELOG.json | 3 +-- 8 files changed, 8 insertions(+), 16 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json index 08075f70e..63ef2fb5c 100644 --- a/packages/0x.js/CHANGELOG.json +++ b/packages/0x.js/CHANGELOG.json @@ -3,8 +3,7 @@ "version": "1.0.1-rc.6", "changes": [ { - "note": - "Fix missing `BlockParamLiteral` type import issue" + "note": "Fix missing `BlockParamLiteral` type import issue" } ] }, diff --git a/packages/connect/CHANGELOG.json b/packages/connect/CHANGELOG.json index 89b1fb728..9d2679abf 100644 --- a/packages/connect/CHANGELOG.json +++ b/packages/connect/CHANGELOG.json @@ -3,8 +3,7 @@ "version": "2.0.0-rc.2", "changes": [ { - "note": - "Dependencies updated" + "note": "Dependencies updated" } ] }, diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index 02d28e68a..2e159961b 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -3,8 +3,7 @@ "version": "1.0.1-rc.5", "changes": [ { - "note": - "Fix missing `BlockParamLiteral` type import issue" + "note": "Fix missing `BlockParamLiteral` type import issue" } ] }, diff --git a/packages/fill-scenarios/CHANGELOG.json b/packages/fill-scenarios/CHANGELOG.json index be739873e..e3dba0296 100644 --- a/packages/fill-scenarios/CHANGELOG.json +++ b/packages/fill-scenarios/CHANGELOG.json @@ -3,8 +3,7 @@ "version": "1.0.1-rc.5", "changes": [ { - "note": - "Dependencies updated" + "note": "Dependencies updated" } ] }, diff --git a/packages/forwarder-helper/CHANGELOG.json b/packages/forwarder-helper/CHANGELOG.json index 6c7305146..e187dc6be 100644 --- a/packages/forwarder-helper/CHANGELOG.json +++ b/packages/forwarder-helper/CHANGELOG.json @@ -3,8 +3,7 @@ "version": "1.0.1-rc.2", "changes": [ { - "note": - "Dependencies updated" + "note": "Dependencies updated" } ] }, diff --git a/packages/json-schemas/CHANGELOG.json b/packages/json-schemas/CHANGELOG.json index caaf88187..205c663fc 100644 --- a/packages/json-schemas/CHANGELOG.json +++ b/packages/json-schemas/CHANGELOG.json @@ -3,8 +3,7 @@ "version": "1.0.1-rc.6", "changes": [ { - "note": - "Dependencies updated" + "note": "Dependencies updated" } ] }, diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index a25a6a82a..a531dd3ae 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -3,8 +3,7 @@ "version": "1.0.1-rc.6", "changes": [ { - "note": - "Fix missing `BlockParamLiteral` type import issue" + "note": "Fix missing `BlockParamLiteral` type import issue" } ] }, diff --git a/packages/order-watcher/CHANGELOG.json b/packages/order-watcher/CHANGELOG.json index 5529bfa9e..445f56e17 100644 --- a/packages/order-watcher/CHANGELOG.json +++ b/packages/order-watcher/CHANGELOG.json @@ -3,8 +3,7 @@ "version": "1.0.1-rc.5", "changes": [ { - "note": - "Fix missing `BlockParamLiteral` type import issue" + "note": "Fix missing `BlockParamLiteral` type import issue" } ] }, -- cgit From 4475fefd07cac0ca166d31526edf4738cca1f16c Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 27 Aug 2018 14:47:56 +0100 Subject: Updated CHANGELOGS --- packages/0x.js/CHANGELOG.json | 3 ++- packages/0x.js/CHANGELOG.md | 8 ++++++++ packages/abi-gen/CHANGELOG.json | 9 +++++++++ packages/abi-gen/CHANGELOG.md | 4 ++++ packages/assert/CHANGELOG.json | 9 +++++++++ packages/assert/CHANGELOG.md | 4 ++++ packages/base-contract/CHANGELOG.json | 9 +++++++++ packages/base-contract/CHANGELOG.md | 4 ++++ packages/connect/CHANGELOG.json | 3 ++- packages/connect/CHANGELOG.md | 4 ++++ packages/contract-wrappers/CHANGELOG.json | 3 ++- packages/contract-wrappers/CHANGELOG.md | 4 ++++ packages/dev-utils/CHANGELOG.json | 9 +++++++++ packages/dev-utils/CHANGELOG.md | 4 ++++ packages/fill-scenarios/CHANGELOG.json | 3 ++- packages/fill-scenarios/CHANGELOG.md | 4 ++++ packages/forwarder-helper/CHANGELOG.json | 3 ++- packages/forwarder-helper/CHANGELOG.md | 4 ++++ packages/json-schemas/CHANGELOG.json | 3 ++- packages/json-schemas/CHANGELOG.md | 4 ++++ packages/migrations/CHANGELOG.json | 9 +++++++++ packages/migrations/CHANGELOG.md | 4 ++++ packages/order-utils/CHANGELOG.json | 3 ++- packages/order-utils/CHANGELOG.md | 8 ++++++++ packages/order-watcher/CHANGELOG.json | 3 ++- packages/order-watcher/CHANGELOG.md | 4 ++++ packages/react-docs/CHANGELOG.json | 9 +++++++++ packages/react-docs/CHANGELOG.md | 4 ++++ packages/react-shared/CHANGELOG.json | 9 +++++++++ packages/react-shared/CHANGELOG.md | 4 ++++ packages/sol-compiler/CHANGELOG.json | 9 +++++++++ packages/sol-compiler/CHANGELOG.md | 4 ++++ packages/sol-cov/CHANGELOG.json | 9 +++++++++ packages/sol-cov/CHANGELOG.md | 4 ++++ packages/sol-resolver/CHANGELOG.json | 9 +++++++++ packages/sol-resolver/CHANGELOG.md | 4 ++++ packages/sra-report/CHANGELOG.json | 9 +++++++++ packages/sra-report/CHANGELOG.md | 4 ++++ packages/sra-spec/CHANGELOG.json | 3 ++- packages/sra-spec/CHANGELOG.md | 5 +++++ packages/subproviders/CHANGELOG.json | 9 +++++++++ packages/subproviders/CHANGELOG.md | 4 ++++ packages/types/CHANGELOG.json | 3 ++- packages/types/CHANGELOG.md | 5 +++++ packages/utils/CHANGELOG.json | 9 +++++++++ packages/utils/CHANGELOG.md | 4 ++++ packages/web3-wrapper/CHANGELOG.json | 9 +++++++++ packages/web3-wrapper/CHANGELOG.md | 4 ++++ 48 files changed, 252 insertions(+), 10 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json index 63ef2fb5c..0a43abe89 100644 --- a/packages/0x.js/CHANGELOG.json +++ b/packages/0x.js/CHANGELOG.json @@ -5,7 +5,8 @@ { "note": "Fix missing `BlockParamLiteral` type import issue" } - ] + ], + "timestamp": 1535377027 }, { "version": "1.0.1-rc.5", diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md index e94907353..65cbbb8fe 100644 --- a/packages/0x.js/CHANGELOG.md +++ b/packages/0x.js/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.1-rc.6 - _August 27, 2018_ + + * Fix missing `BlockParamLiteral` type import issue + +## v1.0.1-rc.5 - _Invalid date_ + + * Fix `main` and `types` package.json entries so that they point to the new location of index.d.ts and index.js + ## v1.0.1-rc.4 - _August 24, 2018_ * Re-organize the exported interface of 0x.js. Remove the `ZeroEx` class, and instead export the same exports as `0x.js`'s sub-packages: `@0xproject/contract-wrappers`, `@0xproject/order-utils` and `@0xproject/order-watcher` (#963) diff --git a/packages/abi-gen/CHANGELOG.json b/packages/abi-gen/CHANGELOG.json index 0b6a9a052..46297a778 100644 --- a/packages/abi-gen/CHANGELOG.json +++ b/packages/abi-gen/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1535377027, + "version": "1.0.7", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1535133899, "version": "1.0.6", diff --git a/packages/abi-gen/CHANGELOG.md b/packages/abi-gen/CHANGELOG.md index 477f69e2c..61f124337 100644 --- a/packages/abi-gen/CHANGELOG.md +++ b/packages/abi-gen/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.7 - _August 27, 2018_ + + * Dependencies updated + ## v1.0.6 - _August 24, 2018_ * Dependencies updated diff --git a/packages/assert/CHANGELOG.json b/packages/assert/CHANGELOG.json index 01fd4c567..b624d56fd 100644 --- a/packages/assert/CHANGELOG.json +++ b/packages/assert/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1535377027, + "version": "1.0.7", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1535133899, "version": "1.0.6", diff --git a/packages/assert/CHANGELOG.md b/packages/assert/CHANGELOG.md index ffa34c48c..fc932cbb3 100644 --- a/packages/assert/CHANGELOG.md +++ b/packages/assert/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.7 - _August 27, 2018_ + + * Dependencies updated + ## v1.0.6 - _August 24, 2018_ * Dependencies updated diff --git a/packages/base-contract/CHANGELOG.json b/packages/base-contract/CHANGELOG.json index 3f01ab0eb..6f801694b 100644 --- a/packages/base-contract/CHANGELOG.json +++ b/packages/base-contract/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1535377027, + "version": "2.0.1", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1535133899, "version": "2.0.0", diff --git a/packages/base-contract/CHANGELOG.md b/packages/base-contract/CHANGELOG.md index 634b3f5a7..530e10ab8 100644 --- a/packages/base-contract/CHANGELOG.md +++ b/packages/base-contract/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.0.1 - _August 27, 2018_ + + * Dependencies updated + ## v2.0.0 - _August 24, 2018_ * Dependencies updated diff --git a/packages/connect/CHANGELOG.json b/packages/connect/CHANGELOG.json index 9d2679abf..8ccb6afe2 100644 --- a/packages/connect/CHANGELOG.json +++ b/packages/connect/CHANGELOG.json @@ -5,7 +5,8 @@ { "note": "Dependencies updated" } - ] + ], + "timestamp": 1535377027 }, { "version": "2.0.0-rc.1", diff --git a/packages/connect/CHANGELOG.md b/packages/connect/CHANGELOG.md index a6821f064..a83d31869 100644 --- a/packages/connect/CHANGELOG.md +++ b/packages/connect/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.0.0-rc.2 - _August 27, 2018_ + + * Dependencies updated + ## v2.0.0-rc.1 - _August 24, 2018_ * Updated for SRA v2 (#974) diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index 2e159961b..b3c9b0fb3 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -5,7 +5,8 @@ { "note": "Fix missing `BlockParamLiteral` type import issue" } - ] + ], + "timestamp": 1535377027 }, { "version": "1.0.1-rc.4", diff --git a/packages/contract-wrappers/CHANGELOG.md b/packages/contract-wrappers/CHANGELOG.md index b1003edfd..fb92ea858 100644 --- a/packages/contract-wrappers/CHANGELOG.md +++ b/packages/contract-wrappers/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.1-rc.5 - _August 27, 2018_ + + * Fix missing `BlockParamLiteral` type import issue + ## v1.0.1-rc.4 - _August 24, 2018_ * Export missing types: `TransactionEncoder`, `ContractAbi`, `JSONRPCRequestPayload`, `JSONRPCResponsePayload`, `JSONRPCErrorCallback`, `AbiDefinition`, `FunctionAbi`, `EventAbi`, `EventParameter`, `DecodedLogArgs`, `MethodAbi`, `ConstructorAbi`, `FallbackAbi`, `DataItem`, `ConstructorStateMutability`, `StateMutability` & `ExchangeSignatureValidatorApprovalEventArgs` (#924) diff --git a/packages/dev-utils/CHANGELOG.json b/packages/dev-utils/CHANGELOG.json index e0f08344b..61403d3c4 100644 --- a/packages/dev-utils/CHANGELOG.json +++ b/packages/dev-utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1535377027, + "version": "1.0.6", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1535133899, "version": "1.0.5", diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 0e39de926..5a1591c8f 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.6 - _August 27, 2018_ + + * Dependencies updated + ## v1.0.5 - _August 24, 2018_ * Dependencies updated diff --git a/packages/fill-scenarios/CHANGELOG.json b/packages/fill-scenarios/CHANGELOG.json index e3dba0296..c86770f3c 100644 --- a/packages/fill-scenarios/CHANGELOG.json +++ b/packages/fill-scenarios/CHANGELOG.json @@ -5,7 +5,8 @@ { "note": "Dependencies updated" } - ] + ], + "timestamp": 1535377027 }, { "version": "1.0.1-rc.4", diff --git a/packages/fill-scenarios/CHANGELOG.md b/packages/fill-scenarios/CHANGELOG.md index 5ae50d1c4..38a57003f 100644 --- a/packages/fill-scenarios/CHANGELOG.md +++ b/packages/fill-scenarios/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.1-rc.5 - _August 27, 2018_ + + * Dependencies updated + ## v1.0.1-rc.4 - _August 24, 2018_ * Dependencies updated diff --git a/packages/forwarder-helper/CHANGELOG.json b/packages/forwarder-helper/CHANGELOG.json index e187dc6be..2d68cf704 100644 --- a/packages/forwarder-helper/CHANGELOG.json +++ b/packages/forwarder-helper/CHANGELOG.json @@ -5,7 +5,8 @@ { "note": "Dependencies updated" } - ] + ], + "timestamp": 1535377027 }, { "version": "1.0.1-rc.1", diff --git a/packages/forwarder-helper/CHANGELOG.md b/packages/forwarder-helper/CHANGELOG.md index 6d48268e6..5be6a6959 100644 --- a/packages/forwarder-helper/CHANGELOG.md +++ b/packages/forwarder-helper/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.1-rc.2 - _August 27, 2018_ + + * Dependencies updated + ## v1.0.1-rc.1 - _August 24, 2018_ * Add initial forwarderHelperFactory (#997) diff --git a/packages/json-schemas/CHANGELOG.json b/packages/json-schemas/CHANGELOG.json index 205c663fc..7bc95f6e6 100644 --- a/packages/json-schemas/CHANGELOG.json +++ b/packages/json-schemas/CHANGELOG.json @@ -5,7 +5,8 @@ { "note": "Dependencies updated" } - ] + ], + "timestamp": 1535377027 }, { "version": "1.0.1-rc.5", diff --git a/packages/json-schemas/CHANGELOG.md b/packages/json-schemas/CHANGELOG.md index e3fa8078d..2a7dcf8db 100644 --- a/packages/json-schemas/CHANGELOG.md +++ b/packages/json-schemas/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.1-rc.6 - _August 27, 2018_ + + * Dependencies updated + ## v1.0.1-rc.5 - _August 24, 2018_ * Update incorrect relayer api fee recipients response schema (#974) diff --git a/packages/migrations/CHANGELOG.json b/packages/migrations/CHANGELOG.json index 17f185a9d..b8994f721 100644 --- a/packages/migrations/CHANGELOG.json +++ b/packages/migrations/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1535377027, + "version": "1.0.6", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1535133899, "version": "1.0.5", diff --git a/packages/migrations/CHANGELOG.md b/packages/migrations/CHANGELOG.md index 0ebd03575..4f378c14c 100644 --- a/packages/migrations/CHANGELOG.md +++ b/packages/migrations/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.6 - _August 27, 2018_ + + * Dependencies updated + ## v1.0.5 - _August 24, 2018_ * Dependencies updated diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index a531dd3ae..f28306f78 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -5,7 +5,8 @@ { "note": "Fix missing `BlockParamLiteral` type import issue" } - ] + ], + "timestamp": 1535377027 }, { "version": "1.0.1-rc.5", diff --git a/packages/order-utils/CHANGELOG.md b/packages/order-utils/CHANGELOG.md index 7af51fc61..c367e4cd8 100644 --- a/packages/order-utils/CHANGELOG.md +++ b/packages/order-utils/CHANGELOG.md @@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.1-rc.6 - _August 27, 2018_ + + * Fix missing `BlockParamLiteral` type import issue + +## v1.0.1-rc.5 - _Invalid date_ + + * Remove Caller and Trezor SignatureTypes (#1015) + ## v1.0.1-rc.4 - _August 24, 2018_ * Remove rounding error being thrown when maker amount is very small (#959) diff --git a/packages/order-watcher/CHANGELOG.json b/packages/order-watcher/CHANGELOG.json index 445f56e17..04ec38fe3 100644 --- a/packages/order-watcher/CHANGELOG.json +++ b/packages/order-watcher/CHANGELOG.json @@ -5,7 +5,8 @@ { "note": "Fix missing `BlockParamLiteral` type import issue" } - ] + ], + "timestamp": 1535377027 }, { "version": "1.0.1-rc.4", diff --git a/packages/order-watcher/CHANGELOG.md b/packages/order-watcher/CHANGELOG.md index 73bfd5114..5f34ea726 100644 --- a/packages/order-watcher/CHANGELOG.md +++ b/packages/order-watcher/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.1-rc.5 - _August 27, 2018_ + + * Fix missing `BlockParamLiteral` type import issue + ## v1.0.1-rc.4 - _August 24, 2018_ * Export types: `ExchangeContractErrs`, `OrderRelevantState`, `JSONRPCRequestPayload`, `JSONRPCErrorCallback` and `JSONRPCResponsePayload` (#924) diff --git a/packages/react-docs/CHANGELOG.json b/packages/react-docs/CHANGELOG.json index 5ca4f4d05..0e41576b6 100644 --- a/packages/react-docs/CHANGELOG.json +++ b/packages/react-docs/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1535377027, + "version": "1.0.7", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1535133899, "version": "1.0.6", diff --git a/packages/react-docs/CHANGELOG.md b/packages/react-docs/CHANGELOG.md index 09d666d75..a06e0f442 100644 --- a/packages/react-docs/CHANGELOG.md +++ b/packages/react-docs/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.7 - _August 27, 2018_ + + * Dependencies updated + ## v1.0.6 - _August 24, 2018_ * Dependencies updated diff --git a/packages/react-shared/CHANGELOG.json b/packages/react-shared/CHANGELOG.json index 01e97f9e7..dc52d8d7f 100644 --- a/packages/react-shared/CHANGELOG.json +++ b/packages/react-shared/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1535377027, + "version": "1.0.8", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1535133899, "version": "1.0.7", diff --git a/packages/react-shared/CHANGELOG.md b/packages/react-shared/CHANGELOG.md index b70db245c..1c5fb93dd 100644 --- a/packages/react-shared/CHANGELOG.md +++ b/packages/react-shared/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.8 - _August 27, 2018_ + + * Dependencies updated + ## v1.0.7 - _August 24, 2018_ * Dependencies updated diff --git a/packages/sol-compiler/CHANGELOG.json b/packages/sol-compiler/CHANGELOG.json index b27253b01..b334e4937 100644 --- a/packages/sol-compiler/CHANGELOG.json +++ b/packages/sol-compiler/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1535377027, + "version": "1.1.1", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "version": "1.1.0", "changes": [ diff --git a/packages/sol-compiler/CHANGELOG.md b/packages/sol-compiler/CHANGELOG.md index 852015ff9..8243a2cf8 100644 --- a/packages/sol-compiler/CHANGELOG.md +++ b/packages/sol-compiler/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.1.1 - _August 27, 2018_ + + * Dependencies updated + ## v1.1.0 - _August 24, 2018_ * Quicken compilation by sending multiple contracts to the same solcjs invocation, batching them together based on compiler version requirements. (#965) diff --git a/packages/sol-cov/CHANGELOG.json b/packages/sol-cov/CHANGELOG.json index 597ba4875..3208eafba 100644 --- a/packages/sol-cov/CHANGELOG.json +++ b/packages/sol-cov/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1535377027, + "version": "2.1.1", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "version": "2.1.0", "changes": [ diff --git a/packages/sol-cov/CHANGELOG.md b/packages/sol-cov/CHANGELOG.md index 819b58c95..72d3a057d 100644 --- a/packages/sol-cov/CHANGELOG.md +++ b/packages/sol-cov/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.1.1 - _August 27, 2018_ + + * Dependencies updated + ## v2.1.0 - _August 24, 2018_ * Export types: `JSONRPCRequestPayload`, `Provider`, `JSONRPCErrorCallback`, `JSONRPCResponsePayload`, `JSONRPCRequestPayloadWithMethod`, `NextCallback`, `ErrorCallback`, `OnNextCompleted` and `Callback` (#924) diff --git a/packages/sol-resolver/CHANGELOG.json b/packages/sol-resolver/CHANGELOG.json index cd1dcbe01..71b7bf99c 100644 --- a/packages/sol-resolver/CHANGELOG.json +++ b/packages/sol-resolver/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1535377027, + "version": "1.0.7", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1535133899, "version": "1.0.6", diff --git a/packages/sol-resolver/CHANGELOG.md b/packages/sol-resolver/CHANGELOG.md index 64ae2d673..2fa21be21 100644 --- a/packages/sol-resolver/CHANGELOG.md +++ b/packages/sol-resolver/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.7 - _August 27, 2018_ + + * Dependencies updated + ## v1.0.6 - _August 24, 2018_ * Dependencies updated diff --git a/packages/sra-report/CHANGELOG.json b/packages/sra-report/CHANGELOG.json index eecb81312..24e219045 100644 --- a/packages/sra-report/CHANGELOG.json +++ b/packages/sra-report/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1535377027, + "version": "1.0.7", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1535133899, "version": "1.0.6", diff --git a/packages/sra-report/CHANGELOG.md b/packages/sra-report/CHANGELOG.md index 3ff6a73ae..e01309f64 100644 --- a/packages/sra-report/CHANGELOG.md +++ b/packages/sra-report/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.7 - _August 27, 2018_ + + * Dependencies updated + ## v1.0.6 - _August 24, 2018_ * Dependencies updated diff --git a/packages/sra-spec/CHANGELOG.json b/packages/sra-spec/CHANGELOG.json index 14830eb05..e9bac0c4d 100644 --- a/packages/sra-spec/CHANGELOG.json +++ b/packages/sra-spec/CHANGELOG.json @@ -9,7 +9,8 @@ { "note": "Fix relative path to introduction MD file" } - ] + ], + "timestamp": 1535377027 }, { "version": "1.0.1-rc.5", diff --git a/packages/sra-spec/CHANGELOG.md b/packages/sra-spec/CHANGELOG.md index 1a88b0ce1..60c1cb78f 100644 --- a/packages/sra-spec/CHANGELOG.md +++ b/packages/sra-spec/CHANGELOG.md @@ -5,6 +5,11 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.1-rc.6 - _August 27, 2018_ + + * Fix `main` and `types` package.json entries so that they point to the new location of index.d.ts and index.js + * Fix relative path to introduction MD file + ## v1.0.1-rc.5 - _August 24, 2018_ * Add takerAddress to /orders parameters (#974) diff --git a/packages/subproviders/CHANGELOG.json b/packages/subproviders/CHANGELOG.json index e11f663e6..03d776499 100644 --- a/packages/subproviders/CHANGELOG.json +++ b/packages/subproviders/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1535377027, + "version": "2.0.1", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "version": "2.0.0", "changes": [ diff --git a/packages/subproviders/CHANGELOG.md b/packages/subproviders/CHANGELOG.md index 444d1997a..c747231be 100644 --- a/packages/subproviders/CHANGELOG.md +++ b/packages/subproviders/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.0.1 - _August 27, 2018_ + + * Dependencies updated + ## v2.0.0 - _August 24, 2018_ * Export types: `PartialTxParams`, `JSONRPCRequestPayloadWithMethod`, `ECSignatureString`, `AccountFetchingConfigs`, `LedgerEthereumClientFactoryAsync`, `OnNextCompleted`, `MnemonicWalletSubproviderConfigs`, LedgerGetAddressResult, `JSONRPCRequestPayload`, `Provider`, `JSONRPCResponsePayload` and `JSONRPCErrorCallback` (#924) diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json index 1210168d4..e96d2a742 100644 --- a/packages/types/CHANGELOG.json +++ b/packages/types/CHANGELOG.json @@ -10,7 +10,8 @@ "note": "Remove Caller and Trezor SignatureTypes", "pr": 1015 } - ] + ], + "timestamp": 1535377027 }, { "version": "1.0.1-rc.5", diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 3bd1d375f..299dcac88 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -5,6 +5,11 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.1-rc.6 - _August 27, 2018_ + + * Add WalletError and ValidatorError revert reasons (#1012) + * Remove Caller and Trezor SignatureTypes (#1015) + ## v1.0.1-rc.5 - _August 24, 2018_ * Add revert reasons for ERC721Token (#933) diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json index 6edafb946..446355007 100644 --- a/packages/utils/CHANGELOG.json +++ b/packages/utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1535377027, + "version": "1.0.7", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1535133899, "version": "1.0.6", diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index a2d4d115b..f7e877a58 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.7 - _August 27, 2018_ + + * Dependencies updated + ## v1.0.6 - _August 24, 2018_ * Dependencies updated diff --git a/packages/web3-wrapper/CHANGELOG.json b/packages/web3-wrapper/CHANGELOG.json index e2bbcaeb8..decefbfea 100644 --- a/packages/web3-wrapper/CHANGELOG.json +++ b/packages/web3-wrapper/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1535377027, + "version": "2.0.1", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "version": "2.0.0", "changes": [ diff --git a/packages/web3-wrapper/CHANGELOG.md b/packages/web3-wrapper/CHANGELOG.md index 3bfe07d3b..8b95de56f 100644 --- a/packages/web3-wrapper/CHANGELOG.md +++ b/packages/web3-wrapper/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.0.1 - _August 27, 2018_ + + * Dependencies updated + ## v2.0.0 - _August 24, 2018_ * Export types: `BlockParam`, `TxData`, `Provider`, `TransactionReceipt`, `Transaction`, `TraceParams`, `TransactionTrace``, BlockWithoutTransactionDat`a, `LogEntry`, `FilterObject`, `CallData`, `TransactionReceiptWithDecodedLogs`, `BlockWithTransactionData``, LogTopi`c, `JSONRPCRequestPayload`, `TransactionReceiptStatus`, `DecodedLogArgs`, `StructLog`, `JSONRPCErrorCallback``, BlockParamLitera`l, `ContractEventArg`, `DecodedLogEntry`, `LogEntryEvent`, `OpCode`, `TxDataPayable`, `JSONRPCResponsePayload``, RawLogEntr`y, `DecodedLogEntryEvent`, `LogWithDecodedArgs`, `AbiDefinition`, `RawLog`, `FunctionAbi`, `EventAbi`, `EventParameter``, MethodAb`i, `ConstructorAbi`, `FallbackAbi`, `DataItem`, `ConstructorStateMutability` and `StateMutability` (#924) -- cgit From 00a4fa5f7c1da8deae703b36b6bbed024cf9e111 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 27 Aug 2018 14:48:24 +0100 Subject: Publish - 0x.js@1.0.1-rc.6 - @0xproject/abi-gen@1.0.7 - @0xproject/assert@1.0.7 - @0xproject/base-contract@2.0.1 - @0xproject/connect@2.0.0-rc.2 - @0xproject/contract-wrappers@1.0.1-rc.5 - contracts@2.1.42 - @0xproject/dev-utils@1.0.6 - @0xproject/fill-scenarios@1.0.1-rc.5 - @0xproject/forwarder-helper@1.0.1-rc.2 - @0xproject/json-schemas@1.0.1-rc.6 - @0xproject/metacoin@0.0.17 - @0xproject/migrations@1.0.6 - @0xproject/monorepo-scripts@1.0.7 - @0xproject/order-utils@1.0.1-rc.6 - @0xproject/order-watcher@1.0.1-rc.5 - @0xproject/react-docs@1.0.7 - @0xproject/react-shared@1.0.8 - @0xproject/sol-compiler@1.1.1 - @0xproject/sol-cov@2.1.1 - @0xproject/sol-resolver@1.0.7 - @0xproject/sra-report@1.0.7 - @0xproject/sra-spec@1.0.1-rc.6 - @0xproject/subproviders@2.0.1 - @0xproject/testnet-faucets@1.0.43 - @0xproject/types@1.0.1-rc.6 - @0xproject/utils@1.0.7 - @0xproject/web3-wrapper@2.0.1 - @0xproject/website@0.0.46 --- packages/0x.js/package.json | 28 +++++++++++++-------------- packages/abi-gen/package.json | 4 ++-- packages/assert/package.json | 6 +++--- packages/base-contract/package.json | 6 +++--- packages/connect/package.json | 10 +++++----- packages/contract-wrappers/package.json | 26 ++++++++++++------------- packages/contracts/package.json | 34 +++++++++++++++------------------ packages/dev-utils/package.json | 10 +++++----- packages/fill-scenarios/package.json | 14 +++++++------- packages/forwarder-helper/package.json | 12 ++++++------ packages/json-schemas/package.json | 4 ++-- packages/metacoin/package.json | 20 +++++++++---------- packages/migrations/package.json | 20 +++++++++---------- packages/monorepo-scripts/package.json | 2 +- packages/order-utils/package.json | 16 ++++++++-------- packages/order-watcher/package.json | 26 ++++++++++++------------- packages/react-docs/package.json | 8 ++++---- packages/react-shared/package.json | 4 ++-- packages/sol-compiler/package.json | 16 ++++++++-------- packages/sol-cov/package.json | 12 ++++++------ packages/sol-resolver/package.json | 4 ++-- packages/sra-report/package.json | 6 +++--- packages/sra-spec/package.json | 4 ++-- packages/subproviders/package.json | 10 +++++----- packages/testnet-faucets/package.json | 8 ++++---- packages/types/package.json | 2 +- packages/utils/package.json | 4 ++-- packages/web3-wrapper/package.json | 8 ++++---- packages/website/package.json | 10 +++++----- 29 files changed, 165 insertions(+), 169 deletions(-) (limited to 'packages') diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 5d4a6ed0e..c3adb496e 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -1,6 +1,6 @@ { "name": "0x.js", - "version": "1.0.1-rc.5", + "version": "1.0.1-rc.6", "engines": { "node": ">=6.12" }, @@ -42,10 +42,10 @@ }, "license": "Apache-2.0", "devDependencies": { - "@0xproject/abi-gen": "^1.0.6", - "@0xproject/dev-utils": "^1.0.5", - "@0xproject/migrations": "^1.0.5", - "@0xproject/monorepo-scripts": "^1.0.6", + "@0xproject/abi-gen": "^1.0.7", + "@0xproject/dev-utils": "^1.0.6", + "@0xproject/migrations": "^1.0.6", + "@0xproject/monorepo-scripts": "^1.0.7", "@0xproject/tslint-config": "^1.0.6", "@types/lodash": "4.14.104", "@types/mocha": "^2.2.42", @@ -73,16 +73,16 @@ "webpack": "^3.1.0" }, "dependencies": { - "@0xproject/assert": "^1.0.6", - "@0xproject/base-contract": "^2.0.0", - "@0xproject/contract-wrappers": "^1.0.1-rc.4", - "@0xproject/order-utils": "^1.0.1-rc.4", - "@0xproject/order-watcher": "^1.0.1-rc.4", - "@0xproject/subproviders": "^2.0.0", - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/assert": "^1.0.7", + "@0xproject/base-contract": "^2.0.1", + "@0xproject/contract-wrappers": "^1.0.1-rc.5", + "@0xproject/order-utils": "^1.0.1-rc.6", + "@0xproject/order-watcher": "^1.0.1-rc.5", + "@0xproject/subproviders": "^2.0.1", + "@0xproject/types": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "ethereum-types": "^1.0.5", "ethers": "3.0.22", "lodash": "^4.17.5", diff --git a/packages/abi-gen/package.json b/packages/abi-gen/package.json index 61a35ef09..000e8cee0 100644 --- a/packages/abi-gen/package.json +++ b/packages/abi-gen/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/abi-gen", - "version": "1.0.6", + "version": "1.0.7", "engines": { "node": ">=6.12" }, @@ -32,7 +32,7 @@ "homepage": "https://github.com/0xProject/0x-monorepo/packages/abi-gen/README.md", "dependencies": { "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", + "@0xproject/utils": "^1.0.7", "chalk": "^2.3.0", "ethereum-types": "^1.0.5", "glob": "^7.1.2", diff --git a/packages/assert/package.json b/packages/assert/package.json index c67a19013..2dea4b616 100644 --- a/packages/assert/package.json +++ b/packages/assert/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/assert", - "version": "1.0.6", + "version": "1.0.7", "engines": { "node": ">=6.12" }, @@ -45,9 +45,9 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/json-schemas": "^1.0.1-rc.5", + "@0xproject/json-schemas": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", + "@0xproject/utils": "^1.0.7", "lodash": "^4.17.5", "valid-url": "^1.0.9" }, diff --git a/packages/base-contract/package.json b/packages/base-contract/package.json index 221222928..33a66c97a 100644 --- a/packages/base-contract/package.json +++ b/packages/base-contract/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/base-contract", - "version": "2.0.0", + "version": "2.0.1", "engines": { "node": ">=6.12" }, @@ -42,8 +42,8 @@ }, "dependencies": { "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "ethereum-types": "^1.0.5", "ethers": "3.0.22", "lodash": "^4.17.5" diff --git a/packages/connect/package.json b/packages/connect/package.json index 116db0469..15f537c7d 100644 --- a/packages/connect/package.json +++ b/packages/connect/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/connect", - "version": "2.0.0-rc.1", + "version": "2.0.0-rc.2", "engines": { "node": ">=6.12" }, @@ -44,11 +44,11 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/connect/README.md", "dependencies": { - "@0xproject/assert": "^1.0.6", - "@0xproject/json-schemas": "^1.0.1-rc.5", - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/assert": "^1.0.7", + "@0xproject/json-schemas": "^1.0.1-rc.6", + "@0xproject/types": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", + "@0xproject/utils": "^1.0.7", "lodash": "^4.17.5", "query-string": "^5.0.1", "sinon": "^4.0.0", diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json index 63fe6a5e5..7d9417d65 100644 --- a/packages/contract-wrappers/package.json +++ b/packages/contract-wrappers/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/contract-wrappers", - "version": "1.0.1-rc.4", + "version": "1.0.1-rc.5", "description": "Smart TS wrappers for 0x smart contracts", "keywords": [ "0xproject", @@ -44,10 +44,10 @@ "node": ">=6.0.0" }, "devDependencies": { - "@0xproject/abi-gen": "^1.0.6", - "@0xproject/dev-utils": "^1.0.5", - "@0xproject/migrations": "^1.0.5", - "@0xproject/subproviders": "^2.0.0", + "@0xproject/abi-gen": "^1.0.7", + "@0xproject/dev-utils": "^1.0.6", + "@0xproject/migrations": "^1.0.6", + "@0xproject/subproviders": "^2.0.1", "@0xproject/tslint-config": "^1.0.6", "@types/lodash": "4.14.104", "@types/mocha": "^2.2.42", @@ -74,15 +74,15 @@ "web3-provider-engine": "14.0.6" }, "dependencies": { - "@0xproject/assert": "^1.0.6", - "@0xproject/base-contract": "^2.0.0", - "@0xproject/fill-scenarios": "^1.0.1-rc.4", - "@0xproject/json-schemas": "^1.0.1-rc.5", - "@0xproject/order-utils": "^1.0.1-rc.4", - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/assert": "^1.0.7", + "@0xproject/base-contract": "^2.0.1", + "@0xproject/fill-scenarios": "^1.0.1-rc.5", + "@0xproject/json-schemas": "^1.0.1-rc.6", + "@0xproject/order-utils": "^1.0.1-rc.6", + "@0xproject/types": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "ethereum-types": "^1.0.5", "ethereumjs-blockstream": "5.0.0", "ethereumjs-util": "^5.1.1", diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 1c1ac81da..5d2f290ac 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "contracts", - "version": "2.1.41", + "version": "2.1.42", "engines": { "node": ">=6.12" }, @@ -20,14 +20,11 @@ "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov", "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", - "run_mocha": - "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", + "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", "compile": "sol-compiler --contracts-dir src", "clean": "shx rm -rf lib generated_contract_wrappers", - "generate_contract_wrappers": - "abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output generated_contract_wrappers --backend ethers", - "lint": - "tslint --project . --exclude **/src/generated_contract_wrappers/**/* --exclude **/lib/**/* && yarn lint-contracts", + "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output generated_contract_wrappers --backend ethers", + "lint": "tslint --project . --exclude **/src/generated_contract_wrappers/**/* --exclude **/lib/**/* && yarn lint-contracts", "coverage:report:text": "istanbul report text", "coverage:report:html": "istanbul report html && open coverage/index.html", "profiler:report:html": "istanbul report html && open coverage/index.html", @@ -36,8 +33,7 @@ "lint-contracts": "solhint src/2.0.0/**/**/**/**/*.sol" }, "config": { - "abis": - "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json" + "abis": "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json" }, "repository": { "type": "git", @@ -50,11 +46,11 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/contracts/README.md", "devDependencies": { - "@0xproject/abi-gen": "^1.0.6", - "@0xproject/dev-utils": "^1.0.5", - "@0xproject/sol-compiler": "^1.1.0", - "@0xproject/sol-cov": "^2.1.0", - "@0xproject/subproviders": "^2.0.0", + "@0xproject/abi-gen": "^1.0.7", + "@0xproject/dev-utils": "^1.0.6", + "@0xproject/sol-compiler": "^1.1.1", + "@0xproject/sol-cov": "^2.1.1", + "@0xproject/subproviders": "^2.0.1", "@0xproject/tslint-config": "^1.0.6", "@types/bn.js": "^4.11.0", "@types/ethereumjs-abi": "^0.6.0", @@ -77,12 +73,12 @@ "yargs": "^10.0.3" }, "dependencies": { - "@0xproject/base-contract": "^2.0.0", - "@0xproject/order-utils": "^1.0.1-rc.4", - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/base-contract": "^2.0.1", + "@0xproject/order-utils": "^1.0.1-rc.6", + "@0xproject/types": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "@types/js-combinatorics": "^0.5.29", "bn.js": "^4.11.8", "ethereum-types": "^1.0.5", diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index a13161320..d4740d3bd 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/dev-utils", - "version": "1.0.5", + "version": "1.0.6", "engines": { "node": ">=6.12" }, @@ -43,11 +43,11 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/subproviders": "^2.0.0", - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/subproviders": "^2.0.1", + "@0xproject/types": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "ethereum-types": "^1.0.5", "lodash": "^4.17.5" }, diff --git a/packages/fill-scenarios/package.json b/packages/fill-scenarios/package.json index 74a95fa43..d0cb29df4 100644 --- a/packages/fill-scenarios/package.json +++ b/packages/fill-scenarios/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/fill-scenarios", - "version": "1.0.1-rc.4", + "version": "1.0.1-rc.5", "description": "0x order fill scenario generator", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -27,7 +27,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/fill-scenarios/README.md", "devDependencies": { - "@0xproject/abi-gen": "^1.0.6", + "@0xproject/abi-gen": "^1.0.7", "@0xproject/tslint-config": "^1.0.6", "@types/lodash": "4.14.104", "copyfiles": "^2.0.0", @@ -38,12 +38,12 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/base-contract": "^2.0.0", - "@0xproject/order-utils": "^1.0.1-rc.4", - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/base-contract": "^2.0.1", + "@0xproject/order-utils": "^1.0.1-rc.6", + "@0xproject/types": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "ethereum-types": "^1.0.5", "ethers": "3.0.22", "lodash": "^4.17.5" diff --git a/packages/forwarder-helper/package.json b/packages/forwarder-helper/package.json index b03041304..fccd7ccdd 100644 --- a/packages/forwarder-helper/package.json +++ b/packages/forwarder-helper/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/forwarder-helper", - "version": "1.0.1-rc.1", + "version": "1.0.1-rc.2", "engines": { "node": ">=6.12" }, @@ -39,12 +39,12 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/forwarder-helper/README.md", "dependencies": { - "@0xproject/assert": "^1.0.6", - "@0xproject/json-schemas": "^1.0.1-rc.5", - "@0xproject/order-utils": "^1.0.1-rc.4", - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/assert": "^1.0.7", + "@0xproject/json-schemas": "^1.0.1-rc.6", + "@0xproject/order-utils": "^1.0.1-rc.6", + "@0xproject/types": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", + "@0xproject/utils": "^1.0.7", "@types/node": "^8.0.53", "lodash": "^4.17.10" }, diff --git a/packages/json-schemas/package.json b/packages/json-schemas/package.json index af4c08672..bdd801a19 100644 --- a/packages/json-schemas/package.json +++ b/packages/json-schemas/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/json-schemas", - "version": "1.0.1-rc.5", + "version": "1.0.1-rc.6", "engines": { "node": ">=6.12" }, @@ -46,7 +46,7 @@ }, "devDependencies": { "@0xproject/tslint-config": "^1.0.6", - "@0xproject/utils": "^1.0.6", + "@0xproject/utils": "^1.0.7", "@types/lodash.foreach": "^4.5.3", "@types/lodash.values": "^4.3.3", "@types/mocha": "^2.2.42", diff --git a/packages/metacoin/package.json b/packages/metacoin/package.json index 7731c1ca3..ea251e2fe 100644 --- a/packages/metacoin/package.json +++ b/packages/metacoin/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/metacoin", - "version": "0.0.16", + "version": "0.0.17", "engines": { "node": ">=6.12" }, @@ -29,15 +29,15 @@ "author": "", "license": "Apache-2.0", "dependencies": { - "@0xproject/abi-gen": "^1.0.6", - "@0xproject/base-contract": "^2.0.0", - "@0xproject/sol-cov": "^2.1.0", - "@0xproject/subproviders": "^2.0.0", + "@0xproject/abi-gen": "^1.0.7", + "@0xproject/base-contract": "^2.0.1", + "@0xproject/sol-cov": "^2.1.1", + "@0xproject/subproviders": "^2.0.1", "@0xproject/tslint-config": "^1.0.6", - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/types": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "@types/mocha": "^5.2.2", "copyfiles": "^2.0.0", "ethereum-types": "^1.0.5", @@ -46,8 +46,8 @@ "run-s": "^0.0.0" }, "devDependencies": { - "@0xproject/dev-utils": "^1.0.5", - "@0xproject/sol-compiler": "^1.1.0", + "@0xproject/dev-utils": "^1.0.6", + "@0xproject/sol-compiler": "^1.1.1", "chai": "^4.0.1", "chai-as-promised": "^7.1.0", "chai-bignumber": "^2.0.1", diff --git a/packages/migrations/package.json b/packages/migrations/package.json index 57f856ce5..89cd4207a 100644 --- a/packages/migrations/package.json +++ b/packages/migrations/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/migrations", - "version": "1.0.5", + "version": "1.0.6", "engines": { "node": ">=6.12" }, @@ -35,10 +35,10 @@ }, "license": "Apache-2.0", "devDependencies": { - "@0xproject/abi-gen": "^1.0.6", - "@0xproject/dev-utils": "^1.0.5", + "@0xproject/abi-gen": "^1.0.7", + "@0xproject/dev-utils": "^1.0.6", "@0xproject/tslint-config": "^1.0.6", - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/types": "^1.0.1-rc.6", "@types/yargs": "^10.0.0", "copyfiles": "^2.0.0", "make-promises-safe": "^1.1.0", @@ -49,13 +49,13 @@ "yargs": "^10.0.3" }, "dependencies": { - "@0xproject/base-contract": "^2.0.0", - "@0xproject/order-utils": "^1.0.1-rc.4", - "@0xproject/sol-compiler": "^1.1.0", - "@0xproject/subproviders": "^2.0.0", + "@0xproject/base-contract": "^2.0.1", + "@0xproject/order-utils": "^1.0.1-rc.6", + "@0xproject/sol-compiler": "^1.1.1", + "@0xproject/subproviders": "^2.0.1", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "@ledgerhq/hw-app-eth": "^4.3.0", "ethereum-types": "^1.0.5", "ethers": "3.0.22", diff --git a/packages/monorepo-scripts/package.json b/packages/monorepo-scripts/package.json index 6a4d0bb45..a2f2343b8 100644 --- a/packages/monorepo-scripts/package.json +++ b/packages/monorepo-scripts/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@0xproject/monorepo-scripts", - "version": "1.0.6", + "version": "1.0.7", "engines": { "node": ">=6.12" }, diff --git a/packages/order-utils/package.json b/packages/order-utils/package.json index 0a35251d7..77a4142fc 100644 --- a/packages/order-utils/package.json +++ b/packages/order-utils/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/order-utils", - "version": "1.0.1-rc.4", + "version": "1.0.1-rc.6", "engines": { "node": ">=6.12" }, @@ -40,7 +40,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/order-utils/README.md", "devDependencies": { - "@0xproject/dev-utils": "^1.0.5", + "@0xproject/dev-utils": "^1.0.6", "@0xproject/tslint-config": "^1.0.6", "@types/bn.js": "^4.11.0", "@types/lodash": "4.14.104", @@ -59,13 +59,13 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/assert": "^1.0.6", - "@0xproject/base-contract": "^2.0.0", - "@0xproject/json-schemas": "^1.0.1-rc.5", - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/assert": "^1.0.7", + "@0xproject/base-contract": "^2.0.1", + "@0xproject/json-schemas": "^1.0.1-rc.6", + "@0xproject/types": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "@types/node": "^8.0.53", "bn.js": "^4.11.8", "ethereum-types": "^1.0.5", diff --git a/packages/order-watcher/package.json b/packages/order-watcher/package.json index 70db15783..0d5bfd48c 100644 --- a/packages/order-watcher/package.json +++ b/packages/order-watcher/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/order-watcher", - "version": "1.0.1-rc.4", + "version": "1.0.1-rc.5", "description": "An order watcher daemon that watches for order validity", "keywords": [ "0x", @@ -43,9 +43,9 @@ "node": ">=6.0.0" }, "devDependencies": { - "@0xproject/abi-gen": "^1.0.6", - "@0xproject/dev-utils": "^1.0.5", - "@0xproject/migrations": "^1.0.5", + "@0xproject/abi-gen": "^1.0.7", + "@0xproject/dev-utils": "^1.0.6", + "@0xproject/migrations": "^1.0.6", "@0xproject/tslint-config": "^1.0.6", "@types/bintrees": "^1.0.2", "@types/lodash": "4.14.104", @@ -71,16 +71,16 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/assert": "^1.0.6", - "@0xproject/base-contract": "^2.0.0", - "@0xproject/contract-wrappers": "^1.0.1-rc.4", - "@0xproject/fill-scenarios": "^1.0.1-rc.4", - "@0xproject/json-schemas": "^1.0.1-rc.5", - "@0xproject/order-utils": "^1.0.1-rc.4", - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/assert": "^1.0.7", + "@0xproject/base-contract": "^2.0.1", + "@0xproject/contract-wrappers": "^1.0.1-rc.5", + "@0xproject/fill-scenarios": "^1.0.1-rc.5", + "@0xproject/json-schemas": "^1.0.1-rc.6", + "@0xproject/order-utils": "^1.0.1-rc.6", + "@0xproject/types": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "bintrees": "^1.0.2", "ethereum-types": "^1.0.5", "ethereumjs-blockstream": "5.0.0", diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json index 47b266e8e..9e95e69d0 100644 --- a/packages/react-docs/package.json +++ b/packages/react-docs/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/react-docs", - "version": "1.0.6", + "version": "1.0.7", "engines": { "node": ">=6.12" }, @@ -24,7 +24,7 @@ "url": "https://github.com/0xProject/0x-monorepo.git" }, "devDependencies": { - "@0xproject/dev-utils": "^1.0.5", + "@0xproject/dev-utils": "^1.0.6", "@0xproject/tslint-config": "^1.0.6", "@types/compare-versions": "^3.0.0", "copyfiles": "^2.0.0", @@ -34,8 +34,8 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/react-shared": "^1.0.7", - "@0xproject/utils": "^1.0.6", + "@0xproject/react-shared": "^1.0.8", + "@0xproject/utils": "^1.0.7", "@types/lodash": "4.14.104", "@types/material-ui": "0.18.0", "@types/node": "^8.0.53", diff --git a/packages/react-shared/package.json b/packages/react-shared/package.json index 27927bbc8..eb6679044 100644 --- a/packages/react-shared/package.json +++ b/packages/react-shared/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/react-shared", - "version": "1.0.7", + "version": "1.0.8", "engines": { "node": ">=6.12" }, @@ -24,7 +24,7 @@ "url": "https://github.com/0xProject/0x-monorepo.git" }, "devDependencies": { - "@0xproject/dev-utils": "^1.0.5", + "@0xproject/dev-utils": "^1.0.6", "@0xproject/tslint-config": "^1.0.6", "copyfiles": "^2.0.0", "make-promises-safe": "^1.1.0", diff --git a/packages/sol-compiler/package.json b/packages/sol-compiler/package.json index f7e901819..f60edc1f9 100644 --- a/packages/sol-compiler/package.json +++ b/packages/sol-compiler/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/sol-compiler", - "version": "1.1.0", + "version": "1.1.1", "engines": { "node": ">=6.12" }, @@ -42,7 +42,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/sol-compiler/README.md", "devDependencies": { - "@0xproject/dev-utils": "^1.0.5", + "@0xproject/dev-utils": "^1.0.6", "@0xproject/tslint-config": "^1.0.6", "@types/mkdirp": "^0.5.2", "@types/require-from-string": "^1.2.0", @@ -65,13 +65,13 @@ "zeppelin-solidity": "1.8.0" }, "dependencies": { - "@0xproject/assert": "^1.0.6", - "@0xproject/json-schemas": "^1.0.1-rc.5", - "@0xproject/sol-resolver": "^1.0.6", - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/assert": "^1.0.7", + "@0xproject/json-schemas": "^1.0.1-rc.6", + "@0xproject/sol-resolver": "^1.0.7", + "@0xproject/types": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "@types/yargs": "^11.0.0", "chalk": "^2.3.0", "ethereum-types": "^1.0.5", diff --git a/packages/sol-cov/package.json b/packages/sol-cov/package.json index 00bb300ad..b8c008b3e 100644 --- a/packages/sol-cov/package.json +++ b/packages/sol-cov/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/sol-cov", - "version": "2.1.0", + "version": "2.1.1", "engines": { "node": ">=6.12" }, @@ -42,12 +42,12 @@ }, "homepage": "https://github.com/0xProject/0x.js/packages/sol-cov/README.md", "dependencies": { - "@0xproject/dev-utils": "^1.0.5", - "@0xproject/sol-compiler": "^1.1.0", - "@0xproject/subproviders": "^2.0.0", + "@0xproject/dev-utils": "^1.0.6", + "@0xproject/sol-compiler": "^1.1.1", + "@0xproject/subproviders": "^2.0.1", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "@types/solidity-parser-antlr": "^0.2.1", "ethereum-types": "^1.0.5", "ethereumjs-util": "^5.1.1", diff --git a/packages/sol-resolver/package.json b/packages/sol-resolver/package.json index 031e9dbdd..f3772a118 100644 --- a/packages/sol-resolver/package.json +++ b/packages/sol-resolver/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/sol-resolver", - "version": "1.0.6", + "version": "1.0.7", "engines": { "node": ">=6.12" }, @@ -31,7 +31,7 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/types": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", "lodash": "^4.17.5" }, diff --git a/packages/sra-report/package.json b/packages/sra-report/package.json index 4311d77fe..91b97dd98 100644 --- a/packages/sra-report/package.json +++ b/packages/sra-report/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/sra-report", - "version": "1.0.6", + "version": "1.0.7", "engines": { "node": ">=6.12" }, @@ -34,13 +34,13 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/sra-report/README.md", "dependencies": { - "@0xproject/assert": "^1.0.6", + "@0xproject/assert": "^1.0.7", "@0xproject/connect": "1.0.4", "@0xproject/json-schemas": "^0.8.3", "@0xproject/order-utils": "^0.0.9", "@0xproject/types": "^0.8.2", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", + "@0xproject/utils": "^1.0.7", "chalk": "^2.3.0", "lodash": "^4.17.5", "newman": "^3.9.3", diff --git a/packages/sra-spec/package.json b/packages/sra-spec/package.json index f0820e136..b560fa77b 100644 --- a/packages/sra-spec/package.json +++ b/packages/sra-spec/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/sra-spec", - "version": "1.0.1-rc.5", + "version": "1.0.1-rc.6", "engines": { "node": ">=6.12" }, @@ -34,7 +34,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/sra-spec/README.md", "dependencies": { - "@0xproject/json-schemas": "^1.0.1-rc.5" + "@0xproject/json-schemas": "^1.0.1-rc.6" }, "devDependencies": { "@0xproject/tslint-config": "^1.0.6", diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json index cf30cc416..48c6d5ae9 100644 --- a/packages/subproviders/package.json +++ b/packages/subproviders/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/subproviders", - "version": "2.0.0", + "version": "2.0.1", "engines": { "node": ">=6.12" }, @@ -29,11 +29,11 @@ } }, "dependencies": { - "@0xproject/assert": "^1.0.6", - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/assert": "^1.0.7", + "@0xproject/types": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "@ledgerhq/hw-app-eth": "^4.3.0", "@ledgerhq/hw-transport-u2f": "^4.3.0", "@types/hdkey": "^0.7.0", diff --git a/packages/testnet-faucets/package.json b/packages/testnet-faucets/package.json index 6dc351c29..9c32dde47 100644 --- a/packages/testnet-faucets/package.json +++ b/packages/testnet-faucets/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@0xproject/testnet-faucets", - "version": "1.0.42", + "version": "1.0.43", "engines": { "node": ">=6.12" }, @@ -19,10 +19,10 @@ "license": "Apache-2.0", "dependencies": { "0x.js": "0.38.5", - "@0xproject/subproviders": "^2.0.0", + "@0xproject/subproviders": "^2.0.1", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "body-parser": "^1.17.1", "ethereum-types": "^1.0.5", "ethereumjs-tx": "^1.3.5", diff --git a/packages/types/package.json b/packages/types/package.json index 6078f422a..291452cbb 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/types", - "version": "1.0.1-rc.5", + "version": "1.0.1-rc.6", "engines": { "node": ">=6.12" }, diff --git a/packages/utils/package.json b/packages/utils/package.json index b4795cefd..c72fb4c83 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/utils", - "version": "1.0.6", + "version": "1.0.7", "engines": { "node": ">=6.12" }, @@ -41,7 +41,7 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/types": "^1.0.1-rc.5", + "@0xproject/types": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", "@types/node": "^8.0.53", "abortcontroller-polyfill": "^1.1.9", diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index fe8e82d15..f84074447 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/web3-wrapper", - "version": "2.0.0", + "version": "2.0.1", "engines": { "node": ">=6.12" }, @@ -53,10 +53,10 @@ "typescript": "3.0.1" }, "dependencies": { - "@0xproject/assert": "^1.0.6", - "@0xproject/json-schemas": "^1.0.1-rc.5", + "@0xproject/assert": "^1.0.7", + "@0xproject/json-schemas": "^1.0.1-rc.6", "@0xproject/typescript-typings": "^1.0.5", - "@0xproject/utils": "^1.0.6", + "@0xproject/utils": "^1.0.7", "ethereum-types": "^1.0.5", "ethereumjs-util": "^5.1.1", "ethers": "3.0.22", diff --git a/packages/website/package.json b/packages/website/package.json index bd6b18866..9b6a0ba36 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/website", - "version": "0.0.45", + "version": "0.0.46", "engines": { "node": ">=6.12" }, @@ -20,13 +20,13 @@ "dependencies": { "@0xproject/contract-wrappers": "^0.0.5", "@0xproject/order-utils": "^0.0.9", - "@0xproject/react-docs": "^1.0.6", + "@0xproject/react-docs": "^1.0.7", "@0xproject/react-shared": "^0.2.3", - "@0xproject/subproviders": "^2.0.0", + "@0xproject/subproviders": "^2.0.1", "@0xproject/types": "^0.8.1", "@0xproject/typescript-typings": "^0.4.3", - "@0xproject/utils": "^1.0.6", - "@0xproject/web3-wrapper": "^2.0.0", + "@0xproject/utils": "^1.0.7", + "@0xproject/web3-wrapper": "^2.0.1", "accounting": "^0.4.1", "basscss": "^8.0.3", "blockies": "^0.0.2", -- cgit From f4a4fefe422a5b2140d5bb394a7f06f842352c69 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 27 Aug 2018 15:02:12 +0100 Subject: Exit with non-error code at end of publishRelease --- packages/monorepo-scripts/src/publish_release_notes.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'packages') diff --git a/packages/monorepo-scripts/src/publish_release_notes.ts b/packages/monorepo-scripts/src/publish_release_notes.ts index 13cf0d85d..a9bc8fe75 100644 --- a/packages/monorepo-scripts/src/publish_release_notes.ts +++ b/packages/monorepo-scripts/src/publish_release_notes.ts @@ -17,6 +17,7 @@ const args = yargs const allUpdatedPackages = await utils.getUpdatedPackagesAsync(shouldIncludePrivate); await publishReleaseNotesAsync(allUpdatedPackages, isDryRun); + process.exit(0); })().catch(err => { utils.log(err); process.exit(1); -- cgit From ff4f86f1d67c29656976d5230ce9aaff0d3166ca Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Mon, 27 Aug 2018 11:19:25 -0700 Subject: fix(contracts): Use correct error message for division by zero --- packages/contracts/test/exchange/internal.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'packages') diff --git a/packages/contracts/test/exchange/internal.ts b/packages/contracts/test/exchange/internal.ts index 2521665c2..de381fca3 100644 --- a/packages/contracts/test/exchange/internal.ts +++ b/packages/contracts/test/exchange/internal.ts @@ -67,9 +67,7 @@ describe('Exchange core internal functions', () => { overflowErrorForSendTransaction = new Error( await getRevertReasonOrErrorMessageForSendTransactionAsync(RevertReason.Uint256Overflow), ); - divisionByZeroErrorForCall = new Error( - await getRevertReasonOrErrorMessageForSendTransactionAsync(RevertReason.DivisionByZero), - ); + divisionByZeroErrorForCall = new Error(RevertReason.DivisionByZero); invalidOpcodeErrorForCall = new Error(await getInvalidOpcodeErrorMessageForCallAsync()); }); // Note(albrow): Don't forget to add beforeEach and afterEach calls to reset -- cgit From f60adbdd723dcf1e5c980c357a8c2c4c337d3a87 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 27 Aug 2018 10:30:37 -0700 Subject: Remove redundant mstores from fillOrderNoThrow --- .../src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol index a7ff400b9..b5b8fc3e0 100644 --- a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol @@ -69,14 +69,7 @@ contract MixinExchangeWrapper is fillOrderCalldata, // write output over input 128 // output size is 128 bytes ) - switch success - case 0 { - mstore(fillResults, 0) - mstore(add(fillResults, 32), 0) - mstore(add(fillResults, 64), 0) - mstore(add(fillResults, 96), 0) - } - case 1 { + if success { mstore(fillResults, mload(fillOrderCalldata)) mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32))) mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64))) -- cgit From 6a99bfa68ebaf43feed2961401fb89fa4eba9cf3 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 27 Aug 2018 12:01:58 -0700 Subject: Add clarifying comments --- .../contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol | 1 + packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol | 1 + 2 files changed, 2 insertions(+) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol index b5b8fc3e0..9e816716c 100644 --- a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol @@ -76,6 +76,7 @@ contract MixinExchangeWrapper is mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96))) } } + // fillResults values will be 0 by default if call was unsuccessful return fillResults; } diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol index 39fa724cc..a5459a21e 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol @@ -96,6 +96,7 @@ contract MixinWrapperFunctions is mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96))) } } + // fillResults values will be 0 by default if call was unsuccessful return fillResults; } -- cgit From 260313a6aeab6f64fa274e9aec5c61c6fcaa64e5 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 23 Aug 2018 15:48:15 -0700 Subject: Initial OrderValidator wrapper --- packages/contract-wrappers/package.json | 2 +- packages/contract-wrappers/src/artifacts.ts | 2 + .../contract-wrappers/src/contract_wrappers.ts | 6 ++ .../contract_wrappers/order_validator_wrapper.ts | 72 ++++++++++++++++++++++ packages/contract-wrappers/src/types.ts | 16 +++++ 5 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts (limited to 'packages') diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json index 7d9417d65..a49a81d61 100644 --- a/packages/contract-wrappers/package.json +++ b/packages/contract-wrappers/package.json @@ -14,7 +14,7 @@ "watch_without_deps": "yarn pre_build && tsc -w", "build": "yarn pre_build && tsc", "pre_build": "run-s update_artifacts_v2_beta update_artifacts_v2 generate_contract_wrappers copy_artifacts", - "generate_contract_wrappers": "abi-gen --abis 'src/artifacts/@(Exchange|DummyERC20Token|DummyERC721Token|ZRXToken|ERC20Token|ERC721Token|WETH9|ERC20Proxy|ERC721Proxy|Forwarder).json' --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers", + "generate_contract_wrappers": "abi-gen --abis 'src/artifacts/@(Exchange|DummyERC20Token|DummyERC721Token|ZRXToken|ERC20Token|ERC721Token|WETH9|ERC20Proxy|ERC721Proxy|Forwarder|OrderValidator).json' --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers", "lint": "tslint --project . --exclude **/src/contract_wrappers/**/* --exclude **/lib/**/*", "test:circleci": "run-s test:coverage", "test": "yarn run_mocha", diff --git a/packages/contract-wrappers/src/artifacts.ts b/packages/contract-wrappers/src/artifacts.ts index 7e67544d2..925f34162 100644 --- a/packages/contract-wrappers/src/artifacts.ts +++ b/packages/contract-wrappers/src/artifacts.ts @@ -8,6 +8,7 @@ import * as ERC721Proxy from './artifacts/ERC721Proxy.json'; import * as ERC721Token from './artifacts/ERC721Token.json'; import * as Exchange from './artifacts/Exchange.json'; import * as Forwarder from './artifacts/Forwarder.json'; +import * as OrderValidator from './artifacts/OrderValidator.json'; import * as EtherToken from './artifacts/WETH9.json'; import * as ZRXToken from './artifacts/ZRXToken.json'; @@ -22,4 +23,5 @@ export const artifacts = { ERC20Proxy: (ERC20Proxy as any) as ContractArtifact, ERC721Proxy: (ERC721Proxy as any) as ContractArtifact, Forwarder: (Forwarder as any) as ContractArtifact, + OrderValidator: (OrderValidator as any) as ContractArtifact, }; diff --git a/packages/contract-wrappers/src/contract_wrappers.ts b/packages/contract-wrappers/src/contract_wrappers.ts index 4277a0746..8ca0fc93c 100644 --- a/packages/contract-wrappers/src/contract_wrappers.ts +++ b/packages/contract-wrappers/src/contract_wrappers.ts @@ -12,6 +12,7 @@ import { ERC721TokenWrapper } from './contract_wrappers/erc721_token_wrapper'; import { EtherTokenWrapper } from './contract_wrappers/ether_token_wrapper'; import { ExchangeWrapper } from './contract_wrappers/exchange_wrapper'; import { ForwarderWrapper } from './contract_wrappers/forwarder_wrapper'; +import { OrderValidatorWrapper } from './contract_wrappers/order_validator_wrapper'; import { ContractWrappersConfigSchema } from './schemas/contract_wrappers_config_schema'; import { contractWrappersPrivateNetworkConfigSchema } from './schemas/contract_wrappers_private_network_config_schema'; import { contractWrappersPublicNetworkConfigSchema } from './schemas/contract_wrappers_public_network_config_schema'; @@ -52,6 +53,10 @@ export class ContractWrappers { * An instance of the ForwarderWrapper class containing methods for interacting with any Forwarder smart contract. */ public forwarder: ForwarderWrapper; + /** + * An instance of the OrderValidatorWrapper class containing methods for interacting with any OrderValidator smart contract. + */ + public orderValidator: OrderValidatorWrapper; private _web3Wrapper: Web3Wrapper; /** @@ -116,6 +121,7 @@ export class ContractWrappers { config.forwarderContractAddress, config.zrxContractAddress, ); + this.orderValidator = new OrderValidatorWrapper(this._web3Wrapper, config.networkId); } /** * Sets a new web3 provider for 0x.js. Updating the provider will stop all diff --git a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts new file mode 100644 index 000000000..6857dcd69 --- /dev/null +++ b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts @@ -0,0 +1,72 @@ +import { schemas } from '@0xproject/json-schemas'; +import { SignedOrder } from '@0xproject/types'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { ContractAbi } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { artifacts } from '../artifacts'; +import { OrdersAndTradersInfo } from '../types'; +import { assert } from '../utils/assert'; + +import { ContractWrapper } from './contract_wrapper'; +import { OrderValidatorContract } from './generated/order_validator'; + +/** + * This class includes the functionality related to interacting with the OrderValidator contract. + */ +export class OrderValidatorWrapper extends ContractWrapper { + public abi: ContractAbi = artifacts.OrderValidator.compilerOutput.abi; + private _orderValidatorContractIfExists?: OrderValidatorContract; + /** + * Instantiate OrderValidatorWrapper + * @param web3Wrapper Web3Wrapper instance to use + * @param networkId Desired networkId + */ + constructor(web3Wrapper: Web3Wrapper, networkId: number) { + super(web3Wrapper, networkId); + } + /** + * Get and object conforming to OrdersAndTradersInfo containing on-chain information of the provided orders and addresses + * @return OrdersAndTradersInfo + */ + public async getOrdersAndTradersInfoAsync( + orders: SignedOrder[], + takerAddresses: string[], + ): Promise { + assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); + _.forEach(takerAddresses, (takerAddress, index) => + assert.isETHAddressHex(`takerAddresses[${index}]`, takerAddress), + ); + assert.assert(orders.length === takerAddresses.length, 'Expected orders.length to equal takerAddresses.length'); + const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync(); + const ordersAndTradersInfo = await OrderValidatorContractInstance.getOrdersAndTradersInfo.callAsync( + orders, + takerAddresses, + ); + const result = { + ordersInfo: ordersAndTradersInfo[0], + tradersInfo: ordersAndTradersInfo[1], + }; + return result; + } + // HACK: We don't want this method to be visible to the other units within that package but not to the end user. + // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused. + // tslint:disable-next-line:no-unused-variable + private _invalidateContractInstance(): void { + delete this._orderValidatorContractIfExists; + } + private async _getOrderValidatorContractAsync(): Promise { + if (!_.isUndefined(this._orderValidatorContractIfExists)) { + return this._orderValidatorContractIfExists; + } + const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(artifacts.OrderValidator); + const contractInstance = new OrderValidatorContract( + abi, + address, + this._web3Wrapper.getProvider(), + this._web3Wrapper.getContractDefaults(), + ); + this._orderValidatorContractIfExists = contractInstance; + return this._orderValidatorContractIfExists; + } +} diff --git a/packages/contract-wrappers/src/types.ts b/packages/contract-wrappers/src/types.ts index 2b3cdc591..efc102a33 100644 --- a/packages/contract-wrappers/src/types.ts +++ b/packages/contract-wrappers/src/types.ts @@ -188,3 +188,19 @@ export enum OrderStatus { FULLY_FILLED, CANCELLED, } + +export interface TraderInfo { + makerBalance: BigNumber; + makerAllowance: BigNumber; + takerBalance: BigNumber; + takerAllowance: BigNumber; + makerZrxBalance: BigNumber; + makerZrxAllowance: BigNumber; + takerZrxBalance: BigNumber; + takerZrxAllowance: BigNumber; +} + +export interface OrdersAndTradersInfo { + ordersInfo: OrderInfo[]; + tradersInfo: TraderInfo[]; +} -- cgit From 898bd75a18f24742aca2ac6beef4208b8f91f98b Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 23 Aug 2018 19:05:03 -0700 Subject: Remove some unused variables in forwarder wrapper test --- packages/contract-wrappers/test/forwarder_wrapper_test.ts | 3 --- 1 file changed, 3 deletions(-) (limited to 'packages') diff --git a/packages/contract-wrappers/test/forwarder_wrapper_test.ts b/packages/contract-wrappers/test/forwarder_wrapper_test.ts index d0b21225c..a969807b2 100644 --- a/packages/contract-wrappers/test/forwarder_wrapper_test.ts +++ b/packages/contract-wrappers/test/forwarder_wrapper_test.ts @@ -25,10 +25,8 @@ describe('ForwarderWrapper', () => { blockPollingIntervalMs: 0, }; const fillableAmount = new BigNumber(5); - const takerTokenFillAmount = new BigNumber(5); let contractWrappers: ContractWrappers; let fillScenarios: FillScenarios; - let forwarderContractAddress: string; let exchangeContractAddress: string; let zrxTokenAddress: string; let userAddresses: string[]; @@ -46,7 +44,6 @@ describe('ForwarderWrapper', () => { before(async () => { await blockchainLifecycle.startAsync(); contractWrappers = new ContractWrappers(provider, contractWrappersConfig); - forwarderContractAddress = contractWrappers.forwarder.getContractAddress(); exchangeContractAddress = contractWrappers.exchange.getContractAddress(); userAddresses = await web3Wrapper.getAvailableAddressesAsync(); zrxTokenAddress = tokenUtils.getProtocolTokenAddress(); -- cgit From 0736c413572e6e55dcc7866a03b8da2eb46a406f Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 23 Aug 2018 19:05:05 -0700 Subject: Add test for order validator --- .../test/order_validator_wrapper_test.ts | 141 +++++++++++++++++++++ packages/contract-wrappers/test/utils/constants.ts | 1 + 2 files changed, 142 insertions(+) create mode 100644 packages/contract-wrappers/test/order_validator_wrapper_test.ts (limited to 'packages') diff --git a/packages/contract-wrappers/test/order_validator_wrapper_test.ts b/packages/contract-wrappers/test/order_validator_wrapper_test.ts new file mode 100644 index 000000000..0226adec7 --- /dev/null +++ b/packages/contract-wrappers/test/order_validator_wrapper_test.ts @@ -0,0 +1,141 @@ +import { BlockchainLifecycle, callbackErrorReporter } from '@0xproject/dev-utils'; +import { FillScenarios } from '@0xproject/fill-scenarios'; +import { assetDataUtils, orderHashUtils } from '@0xproject/order-utils'; +import { DoneCallback, SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import { BlockParamLiteral } from 'ethereum-types'; +import 'mocha'; + +import { ContractWrappers, ExchangeCancelEventArgs, ExchangeEvents, ExchangeFillEventArgs, OrderStatus } from '../src'; +import { DecodedLogEvent, OrderInfo, TraderInfo } from '../src/types'; + +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { tokenUtils } from './utils/token_utils'; +import { provider, web3Wrapper } from './utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('OrderValidator', () => { + const contractWrappersConfig = { + networkId: constants.TESTRPC_NETWORK_ID, + blockPollingIntervalMs: 0, + }; + const fillableAmount = new BigNumber(5); + const partialFillAmount = new BigNumber(2); + let contractWrappers: ContractWrappers; + let fillScenarios: FillScenarios; + let exchangeContractAddress: string; + let zrxTokenAddress: string; + let zrxTokenAssetData: string; + let userAddresses: string[]; + let coinbase: string; + let makerAddress: string; + let takerAddress: string; + let feeRecipient: string; + let anotherMakerAddress: string; + let makerTokenAddress: string; + let takerTokenAddress: string; + let makerAssetData: string; + let takerAssetData: string; + let signedOrder: SignedOrder; + let anotherSignedOrder: SignedOrder; + before(async () => { + await blockchainLifecycle.startAsync(); + contractWrappers = new ContractWrappers(provider, contractWrappersConfig); + exchangeContractAddress = contractWrappers.exchange.getContractAddress(); + userAddresses = await web3Wrapper.getAvailableAddressesAsync(); + zrxTokenAddress = tokenUtils.getProtocolTokenAddress(); + zrxTokenAssetData = assetDataUtils.encodeERC20AssetData(zrxTokenAddress); + fillScenarios = new FillScenarios( + provider, + userAddresses, + zrxTokenAddress, + exchangeContractAddress, + contractWrappers.erc20Proxy.getContractAddress(), + contractWrappers.erc721Proxy.getContractAddress(), + ); + [coinbase, makerAddress, takerAddress, feeRecipient, anotherMakerAddress] = userAddresses; + [makerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); + takerTokenAddress = tokenUtils.getWethTokenAddress(); + [makerAssetData, takerAssetData] = [ + assetDataUtils.encodeERC20AssetData(makerTokenAddress), + assetDataUtils.encodeERC20AssetData(takerTokenAddress), + ]; + + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerAssetData, + takerAssetData, + makerAddress, + constants.NULL_ADDRESS, + fillableAmount, + ); + anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( + zrxTokenAssetData, + takerAssetData, + makerAddress, + constants.NULL_ADDRESS, + fillableAmount, + ); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('#getOrdersAndTradersInfoAsync', () => { + let signedOrders: SignedOrder[]; + let takerAddresses: string[]; + let ordersInfo: OrderInfo[]; + let tradersInfo: TraderInfo[]; + beforeEach(async () => { + signedOrders = [signedOrder, anotherSignedOrder]; + takerAddresses = [takerAddress, takerAddress]; + const ordersAndTradersInfo = await contractWrappers.orderValidator.getOrdersAndTradersInfoAsync( + signedOrders, + takerAddresses, + ); + ordersInfo = ordersAndTradersInfo.ordersInfo; + tradersInfo = ordersAndTradersInfo.tradersInfo; + }); + it('should return the same number of order infos and trader infos as input orders', async () => { + expect(ordersInfo.length).to.be.equal(signedOrders.length); + expect(tradersInfo.length).to.be.equal(takerAddresses.length); + }); + it('should return correct on-chain order info for input orders', async () => { + const firstOrderInfo = ordersInfo[0]; + const secondOrderInfo = ordersInfo[1]; + expect(firstOrderInfo.orderStatus).to.be.equal(OrderStatus.FILLABLE); + expect(firstOrderInfo.orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT); + expect(secondOrderInfo.orderStatus).to.be.equal(OrderStatus.FILLABLE); + expect(secondOrderInfo.orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('should return correct on-chain trader info for input takers', async () => { + const firstTraderInfo = tradersInfo[0]; + const secondTraderInfo = tradersInfo[1]; + expect(firstTraderInfo.makerBalance).to.bignumber.equal(new BigNumber(5)); + expect(firstTraderInfo.makerAllowance).to.bignumber.equal(new BigNumber(5)); + expect(firstTraderInfo.takerBalance).to.bignumber.equal(new BigNumber(0)); + expect(firstTraderInfo.takerAllowance).to.bignumber.equal(new BigNumber(0)); + expect(firstTraderInfo.makerZrxBalance).to.bignumber.equal(new BigNumber(5)); + expect(firstTraderInfo.makerZrxAllowance).to.bignumber.equal(new BigNumber(5)); + expect(firstTraderInfo.takerZrxBalance).to.bignumber.equal(new BigNumber(0)); + expect(firstTraderInfo.takerZrxAllowance).to.bignumber.equal(new BigNumber(0)); + expect(secondTraderInfo.makerBalance).to.bignumber.equal(new BigNumber(5)); + expect(secondTraderInfo.makerAllowance).to.bignumber.equal(new BigNumber(5)); + expect(secondTraderInfo.takerBalance).to.bignumber.equal(new BigNumber(0)); + expect(secondTraderInfo.takerAllowance).to.bignumber.equal(new BigNumber(0)); + expect(secondTraderInfo.makerZrxBalance).to.bignumber.equal(new BigNumber(5)); + expect(secondTraderInfo.makerZrxAllowance).to.bignumber.equal(new BigNumber(5)); + expect(secondTraderInfo.takerZrxBalance).to.bignumber.equal(new BigNumber(0)); + expect(secondTraderInfo.takerZrxAllowance).to.bignumber.equal(new BigNumber(0)); + }); + }); +}); diff --git a/packages/contract-wrappers/test/utils/constants.ts b/packages/contract-wrappers/test/utils/constants.ts index 60c3370a2..f38728b77 100644 --- a/packages/contract-wrappers/test/utils/constants.ts +++ b/packages/contract-wrappers/test/utils/constants.ts @@ -15,4 +15,5 @@ export const constants = { DUMMY_TOKEN_TOTAL_SUPPLY: new BigNumber(10 ** 27), // tslint:disable-line:custom-no-magic-numbers NUM_DUMMY_ERC20_TO_DEPLOY: 3, NUM_DUMMY_ERC721_TO_DEPLOY: 1, + ZERO_AMOUNT: new BigNumber(0), }; -- cgit From d6c670dfcb1bd74f675a9a1cf3b86cfcf6cd85df Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 27 Aug 2018 10:28:07 -0700 Subject: Add getOrderAndTraderInfoAsync to wrapper --- .../contract_wrappers/order_validator_wrapper.ts | 22 ++++++++++++++++++++-- packages/contract-wrappers/src/types.ts | 4 ++++ 2 files changed, 24 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts index 6857dcd69..1e06d191c 100644 --- a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts @@ -5,7 +5,7 @@ import { ContractAbi } from 'ethereum-types'; import * as _ from 'lodash'; import { artifacts } from '../artifacts'; -import { OrdersAndTradersInfo } from '../types'; +import { OrderAndTraderInfo, OrdersAndTradersInfo } from '../types'; import { assert } from '../utils/assert'; import { ContractWrapper } from './contract_wrapper'; @@ -26,7 +26,25 @@ export class OrderValidatorWrapper extends ContractWrapper { super(web3Wrapper, networkId); } /** - * Get and object conforming to OrdersAndTradersInfo containing on-chain information of the provided orders and addresses + * Get and object conforming to OrderAndTraderInfo containing on-chain information of the provided order and address + * @return OrderAndTraderInfo + */ + public async getOrderAndTraderInfoAsync(order: SignedOrder, takerAddress: string): Promise { + assert.doesConformToSchema('order', order, schemas.signedOrderSchema); + assert.isETHAddressHex('takerAddress', takerAddress); + const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync(); + const orderAndTraderInfo = await OrderValidatorContractInstance.getOrderAndTraderInfo.callAsync( + order, + takerAddress, + ); + const result = { + orderInfo: orderAndTraderInfo[0], + traderInfo: orderAndTraderInfo[1], + }; + return result; + } + /** + * Get an object conforming to OrdersAndTradersInfo containing on-chain information of the provided orders and addresses * @return OrdersAndTradersInfo */ public async getOrdersAndTradersInfoAsync( diff --git a/packages/contract-wrappers/src/types.ts b/packages/contract-wrappers/src/types.ts index efc102a33..e73a09fe3 100644 --- a/packages/contract-wrappers/src/types.ts +++ b/packages/contract-wrappers/src/types.ts @@ -204,3 +204,7 @@ export interface OrdersAndTradersInfo { ordersInfo: OrderInfo[]; tradersInfo: TraderInfo[]; } +export interface OrderAndTraderInfo { + orderInfo: OrderInfo; + traderInfo: TraderInfo; +} -- cgit From 68f2dc11b4361af125f9f2ab367d758f673928ea Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 27 Aug 2018 10:54:50 -0700 Subject: Add getTraderInfo and getTradersInfo to wrapper --- .../contract_wrappers/order_validator_wrapper.ts | 29 ++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts index 1e06d191c..dac243434 100644 --- a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts @@ -5,7 +5,7 @@ import { ContractAbi } from 'ethereum-types'; import * as _ from 'lodash'; import { artifacts } from '../artifacts'; -import { OrderAndTraderInfo, OrdersAndTradersInfo } from '../types'; +import { OrderAndTraderInfo, OrdersAndTradersInfo, TraderInfo } from '../types'; import { assert } from '../utils/assert'; import { ContractWrapper } from './contract_wrapper'; @@ -26,7 +26,7 @@ export class OrderValidatorWrapper extends ContractWrapper { super(web3Wrapper, networkId); } /** - * Get and object conforming to OrderAndTraderInfo containing on-chain information of the provided order and address + * Get an object conforming to OrderAndTraderInfo containing on-chain information of the provided order and address * @return OrderAndTraderInfo */ public async getOrderAndTraderInfoAsync(order: SignedOrder, takerAddress: string): Promise { @@ -67,6 +67,31 @@ export class OrderValidatorWrapper extends ContractWrapper { }; return result; } + /** + * Get an object conforming to TraderInfo containing on-chain balance and allowances for maker and taker of order + * @return TraderInfo + */ + public async getTraderInfoAsync(order: SignedOrder, takerAddress: string): Promise { + assert.doesConformToSchema('order', order, schemas.signedOrderSchema); + assert.isETHAddressHex('takerAddress', takerAddress); + const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync(); + const result = await OrderValidatorContractInstance.getTraderInfo.callAsync(order, takerAddress); + return result; + } + /** + * Get an array of objects conforming to TraderInfo containing on-chain balance and allowances for maker and taker of order + * @return array of TraderInfo + */ + public async getTradersInfoAsync(orders: SignedOrder[], takerAddresses: string[]): Promise { + assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); + _.forEach(takerAddresses, (takerAddress, index) => + assert.isETHAddressHex(`takerAddresses[${index}]`, takerAddress), + ); + assert.assert(orders.length === takerAddresses.length, 'Expected orders.length to equal takerAddresses.length'); + const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync(); + const result = await OrderValidatorContractInstance.getTradersInfo.callAsync(orders, takerAddresses); + return result; + } // HACK: We don't want this method to be visible to the other units within that package but not to the end user. // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused. // tslint:disable-next-line:no-unused-variable -- cgit From be2f4cbdcafa19cc8433ff2dc2f0ff6befd64578 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 27 Aug 2018 11:01:53 -0700 Subject: Add getBalanceAndAllowance to wrapper --- .../src/contract_wrappers/order_validator_wrapper.ts | 20 +++++++++++++++++++- packages/contract-wrappers/src/types.ts | 6 ++++++ 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts index dac243434..3b83fc53b 100644 --- a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts @@ -5,7 +5,7 @@ import { ContractAbi } from 'ethereum-types'; import * as _ from 'lodash'; import { artifacts } from '../artifacts'; -import { OrderAndTraderInfo, OrdersAndTradersInfo, TraderInfo } from '../types'; +import { BalanceAndAllowance, OrderAndTraderInfo, OrdersAndTradersInfo, TraderInfo } from '../types'; import { assert } from '../utils/assert'; import { ContractWrapper } from './contract_wrapper'; @@ -92,6 +92,24 @@ export class OrderValidatorWrapper extends ContractWrapper { const result = await OrderValidatorContractInstance.getTradersInfo.callAsync(orders, takerAddresses); return result; } + /** + * Get an object conforming to BalanceAndAllowance containing on-chain balance and allowance for some address and assetData + * @return BalanceAndAllowance + */ + public async getBalanceAndAllowanceAsync(address: string, assetData: string): Promise { + assert.isETHAddressHex('address', address); + assert.isHexString('assetData', assetData); + const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync(); + const balanceAndAllowance = await OrderValidatorContractInstance.getBalanceAndAllowance.callAsync( + address, + assetData, + ); + const result = { + balance: balanceAndAllowance[0], + allowance: balanceAndAllowance[1], + }; + return result; + } // HACK: We don't want this method to be visible to the other units within that package but not to the end user. // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused. // tslint:disable-next-line:no-unused-variable diff --git a/packages/contract-wrappers/src/types.ts b/packages/contract-wrappers/src/types.ts index e73a09fe3..c51e0ae48 100644 --- a/packages/contract-wrappers/src/types.ts +++ b/packages/contract-wrappers/src/types.ts @@ -204,7 +204,13 @@ export interface OrdersAndTradersInfo { ordersInfo: OrderInfo[]; tradersInfo: TraderInfo[]; } + export interface OrderAndTraderInfo { orderInfo: OrderInfo; traderInfo: TraderInfo; } + +export interface BalanceAndAllowance { + balance: BigNumber; + allowance: BigNumber; +} -- cgit From f7469080f929d2364b1621e92857c76cb008bec9 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 27 Aug 2018 12:59:53 -0700 Subject: Update getOrdersAndTradersInfo to return an array instead --- .../src/contract_wrappers/order_validator_wrapper.ts | 19 ++++++++++++------- packages/contract-wrappers/src/types.ts | 5 ----- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'packages') diff --git a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts index 3b83fc53b..854a93bbf 100644 --- a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts @@ -44,13 +44,13 @@ export class OrderValidatorWrapper extends ContractWrapper { return result; } /** - * Get an object conforming to OrdersAndTradersInfo containing on-chain information of the provided orders and addresses - * @return OrdersAndTradersInfo + * Get an array of objects conforming to OrderAndTraderInfo containing on-chain information of the provided orders and addresses + * @return array of OrderAndTraderInfo */ public async getOrdersAndTradersInfoAsync( orders: SignedOrder[], takerAddresses: string[], - ): Promise { + ): Promise { assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema); _.forEach(takerAddresses, (takerAddress, index) => assert.isETHAddressHex(`takerAddresses[${index}]`, takerAddress), @@ -61,10 +61,15 @@ export class OrderValidatorWrapper extends ContractWrapper { orders, takerAddresses, ); - const result = { - ordersInfo: ordersAndTradersInfo[0], - tradersInfo: ordersAndTradersInfo[1], - }; + const orderInfos = ordersAndTradersInfo[0]; + const traderInfos = ordersAndTradersInfo[1]; + const result = _.map(orderInfos, (orderInfo, index) => { + const traderInfo = traderInfos[index]; + return { + orderInfo, + traderInfo, + }; + }); return result; } /** diff --git a/packages/contract-wrappers/src/types.ts b/packages/contract-wrappers/src/types.ts index c51e0ae48..e0b12b7c9 100644 --- a/packages/contract-wrappers/src/types.ts +++ b/packages/contract-wrappers/src/types.ts @@ -200,11 +200,6 @@ export interface TraderInfo { takerZrxAllowance: BigNumber; } -export interface OrdersAndTradersInfo { - ordersInfo: OrderInfo[]; - tradersInfo: TraderInfo[]; -} - export interface OrderAndTraderInfo { orderInfo: OrderInfo; traderInfo: TraderInfo; -- cgit From b0f210dea9b89fddb877dcdb0fd21a5ec08cc0d5 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 27 Aug 2018 13:02:49 -0700 Subject: Add getERC721Owner to wrapper --- .../src/contract_wrappers/order_validator_wrapper.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'packages') diff --git a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts index 854a93bbf..a1e02989d 100644 --- a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts @@ -1,11 +1,12 @@ import { schemas } from '@0xproject/json-schemas'; import { SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { ContractAbi } from 'ethereum-types'; import * as _ from 'lodash'; import { artifacts } from '../artifacts'; -import { BalanceAndAllowance, OrderAndTraderInfo, OrdersAndTradersInfo, TraderInfo } from '../types'; +import { BalanceAndAllowance, OrderAndTraderInfo, TraderInfo } from '../types'; import { assert } from '../utils/assert'; import { ContractWrapper } from './contract_wrapper'; @@ -115,6 +116,17 @@ export class OrderValidatorWrapper extends ContractWrapper { }; return result; } + /** + * Get owner address of tokenId by calling `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token. + * @return Owner of tokenId or null address if unowned + */ + public async getERC721TokenOwnerAsync(tokenAddress: string, tokenId: BigNumber): Promise { + assert.isETHAddressHex('tokenAddress', tokenAddress); + assert.isBigNumber('tokenId', tokenId); + const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync(); + const result = await OrderValidatorContractInstance.getERC721TokenOwner.callAsync(tokenAddress, tokenId); + return result; + } // HACK: We don't want this method to be visible to the other units within that package but not to the end user. // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused. // tslint:disable-next-line:no-unused-variable -- cgit From 38e6d26145d771165263d676ea62d0514191bbe7 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 27 Aug 2018 13:31:12 -0700 Subject: Add params to all function comments --- .../src/contract_wrappers/order_validator_wrapper.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'packages') diff --git a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts index a1e02989d..b33428306 100644 --- a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts @@ -28,6 +28,8 @@ export class OrderValidatorWrapper extends ContractWrapper { } /** * Get an object conforming to OrderAndTraderInfo containing on-chain information of the provided order and address + * @param order An object conforming to SignedOrder + * @param takerAddress An ethereum address * @return OrderAndTraderInfo */ public async getOrderAndTraderInfoAsync(order: SignedOrder, takerAddress: string): Promise { @@ -46,6 +48,8 @@ export class OrderValidatorWrapper extends ContractWrapper { } /** * Get an array of objects conforming to OrderAndTraderInfo containing on-chain information of the provided orders and addresses + * @param orders An array of objects conforming to SignedOrder + * @param takerAddresses An array of ethereum addresses * @return array of OrderAndTraderInfo */ public async getOrdersAndTradersInfoAsync( @@ -75,6 +79,8 @@ export class OrderValidatorWrapper extends ContractWrapper { } /** * Get an object conforming to TraderInfo containing on-chain balance and allowances for maker and taker of order + * @param order An object conforming to SignedOrder + * @param takerAddress An ethereum address * @return TraderInfo */ public async getTraderInfoAsync(order: SignedOrder, takerAddress: string): Promise { @@ -86,6 +92,8 @@ export class OrderValidatorWrapper extends ContractWrapper { } /** * Get an array of objects conforming to TraderInfo containing on-chain balance and allowances for maker and taker of order + * @param orders An array of objects conforming to SignedOrder + * @param takerAddresses An array of ethereum addresses * @return array of TraderInfo */ public async getTradersInfoAsync(orders: SignedOrder[], takerAddresses: string[]): Promise { @@ -100,6 +108,8 @@ export class OrderValidatorWrapper extends ContractWrapper { } /** * Get an object conforming to BalanceAndAllowance containing on-chain balance and allowance for some address and assetData + * @param address An ethereum address + * @param assetData An encoded string that can be decoded by a specified proxy contract * @return BalanceAndAllowance */ public async getBalanceAndAllowanceAsync(address: string, assetData: string): Promise { @@ -118,6 +128,8 @@ export class OrderValidatorWrapper extends ContractWrapper { } /** * Get owner address of tokenId by calling `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token. + * @param tokenAddress An ethereum address + * @param tokenId An ERC721 tokenId * @return Owner of tokenId or null address if unowned */ public async getERC721TokenOwnerAsync(tokenAddress: string, tokenId: BigNumber): Promise { -- cgit From 6c039bbeb1fb0a3120a215b2f52e0259bde2553d Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 27 Aug 2018 13:43:19 -0700 Subject: Update OrderValidator artifact to include getBalancesAndAllowances function --- .../2.0.0-beta-testnet/OrderValidator.json | 175 +++++++++++++-------- 1 file changed, 113 insertions(+), 62 deletions(-) (limited to 'packages') diff --git a/packages/migrations/artifacts/2.0.0-beta-testnet/OrderValidator.json b/packages/migrations/artifacts/2.0.0-beta-testnet/OrderValidator.json index 99805c766..add00d3ce 100644 --- a/packages/migrations/artifacts/2.0.0-beta-testnet/OrderValidator.json +++ b/packages/migrations/artifacts/2.0.0-beta-testnet/OrderValidator.json @@ -410,6 +410,33 @@ "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [ + { + "name": "target", + "type": "address" + }, + { + "name": "assetData", + "type": "bytes[]" + } + ], + "name": "getBalancesAndAllowances", + "outputs": [ + { + "name": "", + "type": "uint256[]" + }, + { + "name": "", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [ @@ -536,15 +563,15 @@ "evm": { "bytecode": { "linkReferences": {}, - "object": "0x60806040523480156200001157600080fd5b5060405162001b0d38038062001b0d833981018060405262000037919081019062000186565b60008054600160a060020a031916600160a060020a03841617905580516200006790600190602084019062000070565b5050506200026b565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620000b357805160ff1916838001178555620000e3565b82800160010185558215620000e3579182015b82811115620000e3578251825591602001919060010190620000c6565b50620000f1929150620000f5565b5090565b6200011291905b80821115620000f15760008155600101620000fc565b90565b60006200012382516200022c565b9392505050565b6000601f820183136200013c57600080fd5b8151620001536200014d8262000204565b620001dd565b915080825260208301602083018583830111156200017057600080fd5b6200017d83828462000238565b50505092915050565b600080604083850312156200019a57600080fd5b6000620001a8858562000115565b92505060208301516001604060020a03811115620001c557600080fd5b620001d3858286016200012a565b9150509250929050565b6040518181016001604060020a0381118282101715620001fc57600080fd5b604052919050565b60006001604060020a038211156200021b57600080fd5b506020601f91909101601f19160190565b600160a060020a031690565b60005b83811015620002555781810151838201526020016200023b565b8381111562000265576000848401525b50505050565b611892806200027b6000396000f3006080604052600436106100775763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166304ad1e53811461007c5780632cd0fc73146100b35780634b95de13146100e1578063690d31141461010f578063b69884631461013c578063f241ffb014610169575b600080fd5b34801561008857600080fd5b5061009c610097366004611118565b610196565b6040516100aa9291906116af565b60405180910390f35b3480156100bf57600080fd5b506100d36100ce366004610fc6565b610263565b6040516100aa9291906116f9565b3480156100ed57600080fd5b506101016100fc36600461107d565b610794565b6040516100aa92919061161a565b34801561011b57600080fd5b5061012f61012a36600461107d565b61086b565b6040516100aa9190611650565b34801561014857600080fd5b5061015c610157366004611018565b610925565b6040516100aa91906115f1565b34801561017557600080fd5b50610189610184366004611118565b610970565b6040516100aa91906116dc565b61019e610bba565b6101a6610bda565b6000546040517fc75e0a8100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063c75e0a81906101fc9087906004016116cb565b606060405180830381600087803b15801561021657600080fd5b505af115801561022a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061024e91908101906110fa565b915061025a8484610970565b90509250929050565b60008080808080808061027c898263ffffffff610a8e16565b955061028f89601063ffffffff610afb16565b6000546040517f6070410800000000000000000000000000000000000000000000000000000000815291965073ffffffffffffffffffffffffffffffffffffffff16906360704108906102e6908990600401611661565b602060405180830381600087803b15801561030057600080fd5b505af1158015610314573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506103389190810190610fa0565b604080517f4552433230546f6b656e28616464726573732900000000000000000000000000815290519081900360130190209094507fffffffff00000000000000000000000000000000000000000000000000000000878116911614156104ed576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8616906370a08231906103eb908d906004016115f1565b602060405180830381600087803b15801561040557600080fd5b505af1158015610419573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061043d919081019061115f565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815290985073ffffffffffffffffffffffffffffffffffffffff86169063dd62ed3e90610494908d9088906004016115ff565b602060405180830381600087803b1580156104ae57600080fd5b505af11580156104c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506104e6919081019061115f565b9650610787565b604080517f455243373231546f6b656e28616464726573732c75696e7432353629000000008152905190819003601c0190207fffffffff000000000000000000000000000000000000000000000000000000008781169116141561074c5761055c89602463ffffffff610b5c16565b92506105688584610925565b91508173ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff16146105a45760006105a7565b60015b60ff1697508473ffffffffffffffffffffffffffffffffffffffff1663e985e9c58b866040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016106039291906115ff565b602060405180830381600087803b15801561061d57600080fd5b505af1158015610631573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061065591908101906110dc565b8061073157508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1663081812fc856040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016106c791906116eb565b602060405180830381600087803b1580156106e157600080fd5b505af11580156106f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506107199190810190610fa0565b73ffffffffffffffffffffffffffffffffffffffff16145b90508061073f576000610742565b60015b60ff169650610787565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161077e9061167f565b60405180910390fd5b5050505050509250929050565b6000546040517f7e9d74dc000000000000000000000000000000000000000000000000000000008152606091829173ffffffffffffffffffffffffffffffffffffffff90911690637e9d74dc906107ef90879060040161163f565b600060405180830381600087803b15801561080957600080fd5b505af115801561081d573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526108639190810190611048565b915061025a84845b606060006060600085519250826040519080825280602002602001820160405280156108b157816020015b61089e610bda565b8152602001906001900390816108965790505b509150600090505b808314610918576108f886828151811015156108d157fe5b9060200190602002015186838151811015156108e957fe5b90602001906020020151610970565b828281518110151561090657fe5b602090810290910101526001016108b9565b8193505b50505092915050565b60006040517f6352211e000000000000000000000000000000000000000000000000000000008152826004820152602081602483875afa801561096757815192505b50505b92915050565b610978610bda565b606061098d8460000151856101400151610263565b602084015282526101608401516109a5908490610263565b60608401526040808401919091526001805482516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101008688161502019094169390930492830181900481028201810190945281815292830182828015610a545780601f10610a2957610100808354040283529160200191610a54565b820191906000526020600020905b815481529060010190602001808311610a3757829003601f168201915b50505050509050610a69846000015182610263565b60a08401526080830152610a7d8382610263565b60e084015260c08301525092915050565b600081600401835110151515610ad0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161077e9061169f565b5050602001517fffffffff000000000000000000000000000000000000000000000000000000001690565b600081601401835110151515610b3d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161077e9061168f565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b6000610b688383610b6f565b9392505050565b600081602001835110151515610bb1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161077e9061166f565b50016020015190565b604080516060810182526000808252602082018190529181019190915290565b6101006040519081016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6000610b6882356117ac565b6000610b6882516117ac565b6000601f82018313610c4957600080fd5b8135610c5c610c578261173b565b611714565b91508181835260208401935060208101905083856020840282011115610c8157600080fd5b60005b83811015610cad5781610c978882610c20565b8452506020928301929190910190600101610c84565b5050505092915050565b6000601f82018313610cc857600080fd5b8151610cd6610c578261173b565b91508181835260208401935060208101905083856060840282011115610cfb57600080fd5b60005b83811015610cad5781610d118882610de4565b84525060209092019160609190910190600101610cfe565b6000601f82018313610d3a57600080fd5b8135610d48610c578261173b565b81815260209384019390925082018360005b83811015610cad5781358601610d708882610e3f565b8452506020928301929190910190600101610d5a565b6000610b6882516117f3565b6000610b6882516117c5565b6000601f82018313610daf57600080fd5b8135610dbd610c578261175c565b91508082526020830160208301858383011115610dd957600080fd5b61091c8382846117f8565b600060608284031215610df657600080fd5b610e006060611714565b90506000610e0e8484610f94565b8252506020610e1f84848301610d92565b6020830152506040610e3384828501610d92565b60408301525092915050565b60006101808284031215610e5257600080fd5b610e5d610180611714565b90506000610e6b8484610c20565b8252506020610e7c84848301610c20565b6020830152506040610e9084828501610c20565b6040830152506060610ea484828501610c20565b6060830152506080610eb884828501610f88565b60808301525060a0610ecc84828501610f88565b60a08301525060c0610ee084828501610f88565b60c08301525060e0610ef484828501610f88565b60e083015250610100610f0984828501610f88565b61010083015250610120610f1f84828501610f88565b6101208301525061014082013567ffffffffffffffff811115610f4157600080fd5b610f4d84828501610d9e565b6101408301525061016082013567ffffffffffffffff811115610f6f57600080fd5b610f7b84828501610d9e565b6101608301525092915050565b6000610b6882356117c5565b6000610b6882516117ed565b600060208284031215610fb257600080fd5b6000610fbe8484610c2c565b949350505050565b60008060408385031215610fd957600080fd5b6000610fe58585610c20565b925050602083013567ffffffffffffffff81111561100257600080fd5b61100e85828601610d9e565b9150509250929050565b6000806040838503121561102b57600080fd5b60006110378585610c20565b925050602061100e85828601610f88565b60006020828403121561105a57600080fd5b815167ffffffffffffffff81111561107157600080fd5b610fbe84828501610cb7565b6000806040838503121561109057600080fd5b823567ffffffffffffffff8111156110a757600080fd5b6110b385828601610d29565b925050602083013567ffffffffffffffff8111156110d057600080fd5b61100e85828601610c38565b6000602082840312156110ee57600080fd5b6000610fbe8484610d86565b60006060828403121561110c57600080fd5b6000610fbe8484610de4565b6000806040838503121561112b57600080fd5b823567ffffffffffffffff81111561114257600080fd5b61114e85828601610e3f565b925050602061100e85828601610c20565b60006020828403121561117157600080fd5b6000610fbe8484610d92565b611186816117ac565b82525050565b6000611197826117a8565b8084526020840193506111a9836117a2565b60005b828110156111d9576111bf868351611411565b6111c8826117a2565b6060969096019591506001016111ac565b5093949350505050565b60006111ee826117a8565b80845260208401935083602082028501611207856117a2565b60005b8481101561123e57838303885261122283835161144e565b925061122d826117a2565b60209890980197915060010161120a565b50909695505050505050565b6000611255826117a8565b808452602084019350611267836117a2565b60005b828110156111d95761127d868351611551565b611286826117a2565b6101009690960195915060010161126a565b611186816117c5565b611186816117c8565b60006112b5826117a8565b8084526112c9816020860160208601611804565b6112d281611830565b9093016020019392505050565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601781527f554e535550504f525445445f41535345545f50524f5859000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b602581527f475245415445525f4f525f455155414c5f544f5f345f4c454e4754485f52455160208201527f5549524544000000000000000000000000000000000000000000000000000000604082015260600190565b8051606083019061142284826115e8565b5060208201516114356020850182611298565b5060408201516114486040850182611298565b50505050565b8051600090610180840190611463858261117d565b506020830151611476602086018261117d565b506040830151611489604086018261117d565b50606083015161149c606086018261117d565b5060808301516114af6080860182611298565b5060a08301516114c260a0860182611298565b5060c08301516114d560c0860182611298565b5060e08301516114e860e0860182611298565b506101008301516114fd610100860182611298565b50610120830151611512610120860182611298565b5061014083015184820361014086015261152c82826112aa565b91505061016083015184820361016086015261154882826112aa565b95945050505050565b80516101008301906115638482611298565b5060208201516115766020850182611298565b5060408201516115896040850182611298565b50606082015161159c6060850182611298565b5060808201516115af6080850182611298565b5060a08201516115c260a0850182611298565b5060c08201516115d560c0850182611298565b5060e082015161144860e0850182611298565b611186816117ed565b6020810161096a828461117d565b6040810161160d828561117d565b610b68602083018461117d565b6040808252810161162b818561118c565b90508181036020830152610fbe818461124a565b60208082528101610b6881846111e3565b60208082528101610b68818461124a565b6020810161096a82846112a1565b6020808252810161096a816112df565b6020808252810161096a81611335565b6020808252810161096a81611365565b6020808252810161096a816113bb565b61016081016116be8285611411565b610b686060830184611551565b60208082528101610b68818461144e565b610100810161096a8284611551565b6020810161096a8284611298565b604081016117078285611298565b610b686020830184611298565b60405181810167ffffffffffffffff8111828210171561173357600080fd5b604052919050565b600067ffffffffffffffff82111561175257600080fd5b5060209081020190565b600067ffffffffffffffff82111561177357600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b60200190565b5190565b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b60ff1690565b151590565b82818337506000910152565b60005b8381101561181f578181015183820152602001611807565b838111156114485750506000910152565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016905600a265627a7a723058200742593843de04ccb442f25f2474ff7a322e1281cdb1232118c18b3985166e7f6c6578706572696d656e74616cf50037", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH3 0x11 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD PUSH3 0x1B0D CODESIZE SUB DUP1 PUSH3 0x1B0D DUP4 CODECOPY DUP2 ADD DUP1 PUSH1 0x40 MSTORE PUSH3 0x37 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH3 0x186 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP5 AND OR SWAP1 SSTORE DUP1 MLOAD PUSH3 0x67 SWAP1 PUSH1 0x1 SWAP1 PUSH1 0x20 DUP5 ADD SWAP1 PUSH3 0x70 JUMP JUMPDEST POP POP POP PUSH3 0x26B JUMP JUMPDEST DUP3 DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 PUSH1 0x1F ADD PUSH1 0x20 SWAP1 DIV DUP2 ADD SWAP3 DUP3 PUSH1 0x1F LT PUSH3 0xB3 JUMPI DUP1 MLOAD PUSH1 0xFF NOT AND DUP4 DUP1 ADD OR DUP6 SSTORE PUSH3 0xE3 JUMP JUMPDEST DUP3 DUP1 ADD PUSH1 0x1 ADD DUP6 SSTORE DUP3 ISZERO PUSH3 0xE3 JUMPI SWAP2 DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH3 0xE3 JUMPI DUP3 MLOAD DUP3 SSTORE SWAP2 PUSH1 0x20 ADD SWAP2 SWAP1 PUSH1 0x1 ADD SWAP1 PUSH3 0xC6 JUMP JUMPDEST POP PUSH3 0xF1 SWAP3 SWAP2 POP PUSH3 0xF5 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH3 0x112 SWAP2 SWAP1 JUMPDEST DUP1 DUP3 GT ISZERO PUSH3 0xF1 JUMPI PUSH1 0x0 DUP2 SSTORE PUSH1 0x1 ADD PUSH3 0xFC JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH3 0x123 DUP3 MLOAD PUSH3 0x22C JUMP JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH3 0x13C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 MLOAD PUSH3 0x153 PUSH3 0x14D DUP3 PUSH3 0x204 JUMP JUMPDEST PUSH3 0x1DD JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH3 0x170 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH3 0x17D DUP4 DUP3 DUP5 PUSH3 0x238 JUMP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH3 0x19A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH3 0x1A8 DUP6 DUP6 PUSH3 0x115 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 DUP4 ADD MLOAD PUSH1 0x1 PUSH1 0x40 PUSH1 0x2 EXP SUB DUP2 GT ISZERO PUSH3 0x1C5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH3 0x1D3 DUP6 DUP3 DUP7 ADD PUSH3 0x12A JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH1 0x1 PUSH1 0x40 PUSH1 0x2 EXP SUB DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH3 0x1FC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1 PUSH1 0x40 PUSH1 0x2 EXP SUB DUP3 GT ISZERO PUSH3 0x21B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH1 0x1F NOT AND ADD SWAP1 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP1 JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH3 0x255 JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH3 0x23B JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH3 0x265 JUMPI PUSH1 0x0 DUP5 DUP5 ADD MSTORE JUMPDEST POP POP POP POP JUMP JUMPDEST PUSH2 0x1892 DUP1 PUSH3 0x27B PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x77 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x4AD1E53 DUP2 EQ PUSH2 0x7C JUMPI DUP1 PUSH4 0x2CD0FC73 EQ PUSH2 0xB3 JUMPI DUP1 PUSH4 0x4B95DE13 EQ PUSH2 0xE1 JUMPI DUP1 PUSH4 0x690D3114 EQ PUSH2 0x10F JUMPI DUP1 PUSH4 0xB6988463 EQ PUSH2 0x13C JUMPI DUP1 PUSH4 0xF241FFB0 EQ PUSH2 0x169 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x88 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x9C PUSH2 0x97 CALLDATASIZE PUSH1 0x4 PUSH2 0x1118 JUMP JUMPDEST PUSH2 0x196 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xAA SWAP3 SWAP2 SWAP1 PUSH2 0x16AF JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xBF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xD3 PUSH2 0xCE CALLDATASIZE PUSH1 0x4 PUSH2 0xFC6 JUMP JUMPDEST PUSH2 0x263 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xAA SWAP3 SWAP2 SWAP1 PUSH2 0x16F9 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xED JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x101 PUSH2 0xFC CALLDATASIZE PUSH1 0x4 PUSH2 0x107D JUMP JUMPDEST PUSH2 0x794 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xAA SWAP3 SWAP2 SWAP1 PUSH2 0x161A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x11B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x12F PUSH2 0x12A CALLDATASIZE PUSH1 0x4 PUSH2 0x107D JUMP JUMPDEST PUSH2 0x86B JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xAA SWAP2 SWAP1 PUSH2 0x1650 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x148 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x15C PUSH2 0x157 CALLDATASIZE PUSH1 0x4 PUSH2 0x1018 JUMP JUMPDEST PUSH2 0x925 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xAA SWAP2 SWAP1 PUSH2 0x15F1 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x175 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x189 PUSH2 0x184 CALLDATASIZE PUSH1 0x4 PUSH2 0x1118 JUMP JUMPDEST PUSH2 0x970 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xAA SWAP2 SWAP1 PUSH2 0x16DC JUMP JUMPDEST PUSH2 0x19E PUSH2 0xBBA JUMP JUMPDEST PUSH2 0x1A6 PUSH2 0xBDA JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x40 MLOAD PUSH32 0xC75E0A8100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP2 AND SWAP1 PUSH4 0xC75E0A81 SWAP1 PUSH2 0x1FC SWAP1 DUP8 SWAP1 PUSH1 0x4 ADD PUSH2 0x16CB JUMP JUMPDEST PUSH1 0x60 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x216 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x22A JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x24E SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x10FA JUMP JUMPDEST SWAP2 POP PUSH2 0x25A DUP5 DUP5 PUSH2 0x970 JUMP JUMPDEST SWAP1 POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP1 DUP1 DUP1 DUP1 DUP1 DUP1 PUSH2 0x27C DUP10 DUP3 PUSH4 0xFFFFFFFF PUSH2 0xA8E AND JUMP JUMPDEST SWAP6 POP PUSH2 0x28F DUP10 PUSH1 0x10 PUSH4 0xFFFFFFFF PUSH2 0xAFB AND JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x40 MLOAD PUSH32 0x6070410800000000000000000000000000000000000000000000000000000000 DUP2 MSTORE SWAP2 SWAP7 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 PUSH4 0x60704108 SWAP1 PUSH2 0x2E6 SWAP1 DUP10 SWAP1 PUSH1 0x4 ADD PUSH2 0x1661 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x300 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x314 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x338 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0xFA0 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x4552433230546F6B656E28616464726573732900000000000000000000000000 DUP2 MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x13 ADD SWAP1 KECCAK256 SWAP1 SWAP5 POP PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP8 DUP2 AND SWAP2 AND EQ ISZERO PUSH2 0x4ED JUMPI PUSH1 0x40 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND SWAP1 PUSH4 0x70A08231 SWAP1 PUSH2 0x3EB SWAP1 DUP14 SWAP1 PUSH1 0x4 ADD PUSH2 0x15F1 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x405 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x419 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x43D SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x115F JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0xDD62ED3E00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE SWAP1 SWAP9 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND SWAP1 PUSH4 0xDD62ED3E SWAP1 PUSH2 0x494 SWAP1 DUP14 SWAP1 DUP9 SWAP1 PUSH1 0x4 ADD PUSH2 0x15FF JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x4AE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x4C2 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x4E6 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x115F JUMP JUMPDEST SWAP7 POP PUSH2 0x787 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x455243373231546F6B656E28616464726573732C75696E743235362900000000 DUP2 MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x1C ADD SWAP1 KECCAK256 PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP8 DUP2 AND SWAP2 AND EQ ISZERO PUSH2 0x74C JUMPI PUSH2 0x55C DUP10 PUSH1 0x24 PUSH4 0xFFFFFFFF PUSH2 0xB5C AND JUMP JUMPDEST SWAP3 POP PUSH2 0x568 DUP6 DUP5 PUSH2 0x925 JUMP JUMPDEST SWAP2 POP DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP11 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ PUSH2 0x5A4 JUMPI PUSH1 0x0 PUSH2 0x5A7 JUMP JUMPDEST PUSH1 0x1 JUMPDEST PUSH1 0xFF AND SWAP8 POP DUP5 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0xE985E9C5 DUP12 DUP7 PUSH1 0x40 MLOAD DUP4 PUSH4 0xFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x603 SWAP3 SWAP2 SWAP1 PUSH2 0x15FF JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x61D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x631 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x655 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x10DC JUMP JUMPDEST DUP1 PUSH2 0x731 JUMPI POP DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0x81812FC DUP6 PUSH1 0x40 MLOAD DUP3 PUSH4 0xFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C7 SWAP2 SWAP1 PUSH2 0x16EB JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x6E1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x6F5 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x719 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0xFA0 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ JUMPDEST SWAP1 POP DUP1 PUSH2 0x73F JUMPI PUSH1 0x0 PUSH2 0x742 JUMP JUMPDEST PUSH1 0x1 JUMPDEST PUSH1 0xFF AND SWAP7 POP PUSH2 0x787 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x77E SWAP1 PUSH2 0x167F JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST POP POP POP POP POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x40 MLOAD PUSH32 0x7E9D74DC00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x60 SWAP2 DUP3 SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP2 AND SWAP1 PUSH4 0x7E9D74DC SWAP1 PUSH2 0x7EF SWAP1 DUP8 SWAP1 PUSH1 0x4 ADD PUSH2 0x163F JUMP JUMPDEST PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x809 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x81D JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x0 DUP3 RETURNDATACOPY PUSH1 0x1F RETURNDATASIZE SWAP1 DUP2 ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND DUP3 ADD PUSH1 0x40 MSTORE PUSH2 0x863 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x1048 JUMP JUMPDEST SWAP2 POP PUSH2 0x25A DUP5 DUP5 JUMPDEST PUSH1 0x60 PUSH1 0x0 PUSH1 0x60 PUSH1 0x0 DUP6 MLOAD SWAP3 POP DUP3 PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0x8B1 JUMPI DUP2 PUSH1 0x20 ADD JUMPDEST PUSH2 0x89E PUSH2 0xBDA JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD SWAP1 PUSH1 0x1 SWAP1 SUB SWAP1 DUP2 PUSH2 0x896 JUMPI SWAP1 POP JUMPDEST POP SWAP2 POP PUSH1 0x0 SWAP1 POP JUMPDEST DUP1 DUP4 EQ PUSH2 0x918 JUMPI PUSH2 0x8F8 DUP7 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x8D1 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP7 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x8E9 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x970 JUMP JUMPDEST DUP3 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x906 JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MSTORE PUSH1 0x1 ADD PUSH2 0x8B9 JUMP JUMPDEST DUP2 SWAP4 POP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x40 MLOAD PUSH32 0x6352211E00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE DUP3 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x20 DUP2 PUSH1 0x24 DUP4 DUP8 GAS STATICCALL DUP1 ISZERO PUSH2 0x967 JUMPI DUP2 MLOAD SWAP3 POP JUMPDEST POP POP JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH2 0x978 PUSH2 0xBDA JUMP JUMPDEST PUSH1 0x60 PUSH2 0x98D DUP5 PUSH1 0x0 ADD MLOAD DUP6 PUSH2 0x140 ADD MLOAD PUSH2 0x263 JUMP JUMPDEST PUSH1 0x20 DUP5 ADD MSTORE DUP3 MSTORE PUSH2 0x160 DUP5 ADD MLOAD PUSH2 0x9A5 SWAP1 DUP5 SWAP1 PUSH2 0x263 JUMP JUMPDEST PUSH1 0x60 DUP5 ADD MSTORE PUSH1 0x40 DUP1 DUP5 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x1 DUP1 SLOAD DUP3 MLOAD PUSH1 0x20 PUSH1 0x1F PUSH1 0x2 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH2 0x100 DUP7 DUP9 AND ISZERO MUL ADD SWAP1 SWAP5 AND SWAP4 SWAP1 SWAP4 DIV SWAP3 DUP4 ADD DUP2 SWAP1 DIV DUP2 MUL DUP3 ADD DUP2 ADD SWAP1 SWAP5 MSTORE DUP2 DUP2 MSTORE SWAP3 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0xA54 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0xA29 JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0xA54 JUMP JUMPDEST DUP3 ADD SWAP2 SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD DUP2 MSTORE SWAP1 PUSH1 0x1 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0xA37 JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP POP POP SWAP1 POP PUSH2 0xA69 DUP5 PUSH1 0x0 ADD MLOAD DUP3 PUSH2 0x263 JUMP JUMPDEST PUSH1 0xA0 DUP5 ADD MSTORE PUSH1 0x80 DUP4 ADD MSTORE PUSH2 0xA7D DUP4 DUP3 PUSH2 0x263 JUMP JUMPDEST PUSH1 0xE0 DUP5 ADD MSTORE PUSH1 0xC0 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x4 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0xAD0 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x77E SWAP1 PUSH2 0x169F JUMP JUMPDEST POP POP PUSH1 0x20 ADD MLOAD PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x14 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0xB3D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x77E SWAP1 PUSH2 0x168F JUMP JUMPDEST POP ADD PUSH1 0x14 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xB68 DUP4 DUP4 PUSH2 0xB6F JUMP JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x20 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0xBB1 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x77E SWAP1 PUSH2 0x166F JUMP JUMPDEST POP ADD PUSH1 0x20 ADD MLOAD SWAP1 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x60 DUP2 ADD DUP3 MSTORE PUSH1 0x0 DUP1 DUP3 MSTORE PUSH1 0x20 DUP3 ADD DUP2 SWAP1 MSTORE SWAP2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE SWAP1 JUMP JUMPDEST PUSH2 0x100 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xB68 DUP3 CALLDATALOAD PUSH2 0x17AC JUMP JUMPDEST PUSH1 0x0 PUSH2 0xB68 DUP3 MLOAD PUSH2 0x17AC JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xC49 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xC5C PUSH2 0xC57 DUP3 PUSH2 0x173B JUMP JUMPDEST PUSH2 0x1714 JUMP JUMPDEST SWAP2 POP DUP2 DUP2 DUP4 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH1 0x20 DUP2 ADD SWAP1 POP DUP4 DUP6 PUSH1 0x20 DUP5 MUL DUP3 ADD GT ISZERO PUSH2 0xC81 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xCAD JUMPI DUP2 PUSH2 0xC97 DUP9 DUP3 PUSH2 0xC20 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0xC84 JUMP JUMPDEST POP POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xCC8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 MLOAD PUSH2 0xCD6 PUSH2 0xC57 DUP3 PUSH2 0x173B JUMP JUMPDEST SWAP2 POP DUP2 DUP2 DUP4 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH1 0x20 DUP2 ADD SWAP1 POP DUP4 DUP6 PUSH1 0x60 DUP5 MUL DUP3 ADD GT ISZERO PUSH2 0xCFB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xCAD JUMPI DUP2 PUSH2 0xD11 DUP9 DUP3 PUSH2 0xDE4 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x60 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0xCFE JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xD3A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xD48 PUSH2 0xC57 DUP3 PUSH2 0x173B JUMP JUMPDEST DUP2 DUP2 MSTORE PUSH1 0x20 SWAP4 DUP5 ADD SWAP4 SWAP1 SWAP3 POP DUP3 ADD DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xCAD JUMPI DUP2 CALLDATALOAD DUP7 ADD PUSH2 0xD70 DUP9 DUP3 PUSH2 0xE3F JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0xD5A JUMP JUMPDEST PUSH1 0x0 PUSH2 0xB68 DUP3 MLOAD PUSH2 0x17F3 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xB68 DUP3 MLOAD PUSH2 0x17C5 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xDAF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xDBD PUSH2 0xC57 DUP3 PUSH2 0x175C JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH2 0xDD9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x91C DUP4 DUP3 DUP5 PUSH2 0x17F8 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x60 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xDF6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xE00 PUSH1 0x60 PUSH2 0x1714 JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0xE0E DUP5 DUP5 PUSH2 0xF94 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0xE1F DUP5 DUP5 DUP4 ADD PUSH2 0xD92 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0xE33 DUP5 DUP3 DUP6 ADD PUSH2 0xD92 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x180 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xE52 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xE5D PUSH2 0x180 PUSH2 0x1714 JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0xE6B DUP5 DUP5 PUSH2 0xC20 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0xE7C DUP5 DUP5 DUP4 ADD PUSH2 0xC20 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0xE90 DUP5 DUP3 DUP6 ADD PUSH2 0xC20 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP PUSH1 0x60 PUSH2 0xEA4 DUP5 DUP3 DUP6 ADD PUSH2 0xC20 JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MSTORE POP PUSH1 0x80 PUSH2 0xEB8 DUP5 DUP3 DUP6 ADD PUSH2 0xF88 JUMP JUMPDEST PUSH1 0x80 DUP4 ADD MSTORE POP PUSH1 0xA0 PUSH2 0xECC DUP5 DUP3 DUP6 ADD PUSH2 0xF88 JUMP JUMPDEST PUSH1 0xA0 DUP4 ADD MSTORE POP PUSH1 0xC0 PUSH2 0xEE0 DUP5 DUP3 DUP6 ADD PUSH2 0xF88 JUMP JUMPDEST PUSH1 0xC0 DUP4 ADD MSTORE POP PUSH1 0xE0 PUSH2 0xEF4 DUP5 DUP3 DUP6 ADD PUSH2 0xF88 JUMP JUMPDEST PUSH1 0xE0 DUP4 ADD MSTORE POP PUSH2 0x100 PUSH2 0xF09 DUP5 DUP3 DUP6 ADD PUSH2 0xF88 JUMP JUMPDEST PUSH2 0x100 DUP4 ADD MSTORE POP PUSH2 0x120 PUSH2 0xF1F DUP5 DUP3 DUP6 ADD PUSH2 0xF88 JUMP JUMPDEST PUSH2 0x120 DUP4 ADD MSTORE POP PUSH2 0x140 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xF41 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xF4D DUP5 DUP3 DUP6 ADD PUSH2 0xD9E JUMP JUMPDEST PUSH2 0x140 DUP4 ADD MSTORE POP PUSH2 0x160 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xF6F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xF7B DUP5 DUP3 DUP6 ADD PUSH2 0xD9E JUMP JUMPDEST PUSH2 0x160 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0xB68 DUP3 CALLDATALOAD PUSH2 0x17C5 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xB68 DUP3 MLOAD PUSH2 0x17ED JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xFB2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xFBE DUP5 DUP5 PUSH2 0xC2C JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0xFD9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xFE5 DUP6 DUP6 PUSH2 0xC20 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 DUP4 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x1002 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x100E DUP6 DUP3 DUP7 ADD PUSH2 0xD9E JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x102B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x1037 DUP6 DUP6 PUSH2 0xC20 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x100E DUP6 DUP3 DUP7 ADD PUSH2 0xF88 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x105A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 MLOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x1071 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xFBE DUP5 DUP3 DUP6 ADD PUSH2 0xCB7 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x1090 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP3 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x10A7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x10B3 DUP6 DUP3 DUP7 ADD PUSH2 0xD29 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 DUP4 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x10D0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x100E DUP6 DUP3 DUP7 ADD PUSH2 0xC38 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x10EE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xFBE DUP5 DUP5 PUSH2 0xD86 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x60 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x110C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xFBE DUP5 DUP5 PUSH2 0xDE4 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x112B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP3 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x1142 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x114E DUP6 DUP3 DUP7 ADD PUSH2 0xE3F JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x100E DUP6 DUP3 DUP7 ADD PUSH2 0xC20 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x1171 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xFBE DUP5 DUP5 PUSH2 0xD92 JUMP JUMPDEST PUSH2 0x1186 DUP2 PUSH2 0x17AC JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x1197 DUP3 PUSH2 0x17A8 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH2 0x11A9 DUP4 PUSH2 0x17A2 JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x11D9 JUMPI PUSH2 0x11BF DUP7 DUP4 MLOAD PUSH2 0x1411 JUMP JUMPDEST PUSH2 0x11C8 DUP3 PUSH2 0x17A2 JUMP JUMPDEST PUSH1 0x60 SWAP7 SWAP1 SWAP7 ADD SWAP6 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x11AC JUMP JUMPDEST POP SWAP4 SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x11EE DUP3 PUSH2 0x17A8 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP DUP4 PUSH1 0x20 DUP3 MUL DUP6 ADD PUSH2 0x1207 DUP6 PUSH2 0x17A2 JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP5 DUP2 LT ISZERO PUSH2 0x123E JUMPI DUP4 DUP4 SUB DUP9 MSTORE PUSH2 0x1222 DUP4 DUP4 MLOAD PUSH2 0x144E JUMP JUMPDEST SWAP3 POP PUSH2 0x122D DUP3 PUSH2 0x17A2 JUMP JUMPDEST PUSH1 0x20 SWAP9 SWAP1 SWAP9 ADD SWAP8 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x120A JUMP JUMPDEST POP SWAP1 SWAP7 SWAP6 POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x1255 DUP3 PUSH2 0x17A8 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH2 0x1267 DUP4 PUSH2 0x17A2 JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x11D9 JUMPI PUSH2 0x127D DUP7 DUP4 MLOAD PUSH2 0x1551 JUMP JUMPDEST PUSH2 0x1286 DUP3 PUSH2 0x17A2 JUMP JUMPDEST PUSH2 0x100 SWAP7 SWAP1 SWAP7 ADD SWAP6 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x126A JUMP JUMPDEST PUSH2 0x1186 DUP2 PUSH2 0x17C5 JUMP JUMPDEST PUSH2 0x1186 DUP2 PUSH2 0x17C8 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x12B5 DUP3 PUSH2 0x17A8 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH2 0x12C9 DUP2 PUSH1 0x20 DUP7 ADD PUSH1 0x20 DUP7 ADD PUSH2 0x1804 JUMP JUMPDEST PUSH2 0x12D2 DUP2 PUSH2 0x1830 JUMP JUMPDEST SWAP1 SWAP4 ADD PUSH1 0x20 ADD SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F33325F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x17 DUP2 MSTORE PUSH32 0x554E535550504F525445445F41535345545F50524F5859000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F32305F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x25 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F345F4C454E4754485F524551 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5549524544000000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST DUP1 MLOAD PUSH1 0x60 DUP4 ADD SWAP1 PUSH2 0x1422 DUP5 DUP3 PUSH2 0x15E8 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x1435 PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x1448 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP POP POP POP JUMP JUMPDEST DUP1 MLOAD PUSH1 0x0 SWAP1 PUSH2 0x180 DUP5 ADD SWAP1 PUSH2 0x1463 DUP6 DUP3 PUSH2 0x117D JUMP JUMPDEST POP PUSH1 0x20 DUP4 ADD MLOAD PUSH2 0x1476 PUSH1 0x20 DUP7 ADD DUP3 PUSH2 0x117D JUMP JUMPDEST POP PUSH1 0x40 DUP4 ADD MLOAD PUSH2 0x1489 PUSH1 0x40 DUP7 ADD DUP3 PUSH2 0x117D JUMP JUMPDEST POP PUSH1 0x60 DUP4 ADD MLOAD PUSH2 0x149C PUSH1 0x60 DUP7 ADD DUP3 PUSH2 0x117D JUMP JUMPDEST POP PUSH1 0x80 DUP4 ADD MLOAD PUSH2 0x14AF PUSH1 0x80 DUP7 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0xA0 DUP4 ADD MLOAD PUSH2 0x14C2 PUSH1 0xA0 DUP7 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0xC0 DUP4 ADD MLOAD PUSH2 0x14D5 PUSH1 0xC0 DUP7 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0xE0 DUP4 ADD MLOAD PUSH2 0x14E8 PUSH1 0xE0 DUP7 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH2 0x100 DUP4 ADD MLOAD PUSH2 0x14FD PUSH2 0x100 DUP7 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH2 0x120 DUP4 ADD MLOAD PUSH2 0x1512 PUSH2 0x120 DUP7 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH2 0x140 DUP4 ADD MLOAD DUP5 DUP3 SUB PUSH2 0x140 DUP7 ADD MSTORE PUSH2 0x152C DUP3 DUP3 PUSH2 0x12AA JUMP JUMPDEST SWAP2 POP POP PUSH2 0x160 DUP4 ADD MLOAD DUP5 DUP3 SUB PUSH2 0x160 DUP7 ADD MSTORE PUSH2 0x1548 DUP3 DUP3 PUSH2 0x12AA JUMP JUMPDEST SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST DUP1 MLOAD PUSH2 0x100 DUP4 ADD SWAP1 PUSH2 0x1563 DUP5 DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x1576 PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x1589 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0x60 DUP3 ADD MLOAD PUSH2 0x159C PUSH1 0x60 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0x80 DUP3 ADD MLOAD PUSH2 0x15AF PUSH1 0x80 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0xA0 DUP3 ADD MLOAD PUSH2 0x15C2 PUSH1 0xA0 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0xC0 DUP3 ADD MLOAD PUSH2 0x15D5 PUSH1 0xC0 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0xE0 DUP3 ADD MLOAD PUSH2 0x1448 PUSH1 0xE0 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST PUSH2 0x1186 DUP2 PUSH2 0x17ED JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x96A DUP3 DUP5 PUSH2 0x117D JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x160D DUP3 DUP6 PUSH2 0x117D JUMP JUMPDEST PUSH2 0xB68 PUSH1 0x20 DUP4 ADD DUP5 PUSH2 0x117D JUMP JUMPDEST PUSH1 0x40 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x162B DUP2 DUP6 PUSH2 0x118C JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0xFBE DUP2 DUP5 PUSH2 0x124A JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xB68 DUP2 DUP5 PUSH2 0x11E3 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xB68 DUP2 DUP5 PUSH2 0x124A JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x96A DUP3 DUP5 PUSH2 0x12A1 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x96A DUP2 PUSH2 0x12DF JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x96A DUP2 PUSH2 0x1335 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x96A DUP2 PUSH2 0x1365 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x96A DUP2 PUSH2 0x13BB JUMP JUMPDEST PUSH2 0x160 DUP2 ADD PUSH2 0x16BE DUP3 DUP6 PUSH2 0x1411 JUMP JUMPDEST PUSH2 0xB68 PUSH1 0x60 DUP4 ADD DUP5 PUSH2 0x1551 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xB68 DUP2 DUP5 PUSH2 0x144E JUMP JUMPDEST PUSH2 0x100 DUP2 ADD PUSH2 0x96A DUP3 DUP5 PUSH2 0x1551 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x96A DUP3 DUP5 PUSH2 0x1298 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x1707 DUP3 DUP6 PUSH2 0x1298 JUMP JUMPDEST PUSH2 0xB68 PUSH1 0x20 DUP4 ADD DUP5 PUSH2 0x1298 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0x1733 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x1752 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 SWAP1 DUP2 MUL ADD SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x1773 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND ADD SWAP1 JUMP JUMPDEST PUSH1 0x20 ADD SWAP1 JUMP JUMPDEST MLOAD SWAP1 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND SWAP1 JUMP JUMPDEST PUSH1 0xFF AND SWAP1 JUMP JUMPDEST ISZERO ISZERO SWAP1 JUMP JUMPDEST DUP3 DUP2 DUP4 CALLDATACOPY POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x181F JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x1807 JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0x1448 JUMPI POP POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x1F ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND SWAP1 JUMP STOP LOG2 PUSH6 0x627A7A723058 KECCAK256 SMOD TIMESTAMP MSIZE CODESIZE NUMBER 0xde DIV 0xcc 0xb4 TIMESTAMP CALLCODE 0x5f 0x24 PUSH21 0xFF7A322E1281CDB1232118C18B3985166E7F6C6578 PUSH17 0x6572696D656E74616CF500370000000000 ", - "sourceMap": "898:7576:11:-;;;1916:167;8:9:-1;5:2;;;30:1;27;20:12;5:2;1916:167:11;;;;;;;;;;;;;;;;;;;;;;;;2005:8;:31;;-1:-1:-1;;;;;;2005:31:11;-1:-1:-1;;;;;2005:31:11;;;;;2046:30;;;;-1:-1:-1;;2046:30:11;;;;;:::i;:::-;;1916:167;;898:7576;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;898:7576:11;;;-1:-1:-1;898:7576:11;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::o;5:122:-1:-;;83:39;114:6;108:13;83:39;;;74:48;68:59;-1:-1;;;68:59;135:442;;240:4;228:17;;224:27;-1:-1;214:2;;265:1;262;255:12;214:2;295:6;289:13;317:64;332:48;373:6;332:48;;;317:64;;;308:73;;401:6;394:5;387:21;437:4;429:6;425:17;470:4;463:5;459:16;505:3;496:6;491:3;487:16;484:25;481:2;;;522:1;519;512:12;481:2;532:39;564:6;559:3;554;532:39;;;207:370;;;;;;;;585:496;;;726:2;714:9;705:7;701:23;697:32;694:2;;;742:1;739;732:12;694:2;777:1;794:64;850:7;830:9;794:64;;;784:74;;756:108;916:2;905:9;901:18;895:25;-1:-1;;;;;932:6;929:30;926:2;;;972:1;969;962:12;926:2;992:73;1057:7;1048:6;1037:9;1033:22;992:73;;;982:83;;874:197;688:393;;;;;;1088:256;1150:2;1144:9;1176:17;;;-1:-1;;;;;1236:34;;1272:22;;;1233:62;1230:2;;;1308:1;1305;1298:12;1230:2;1324;1317:22;1128:216;;-1:-1;1128:216;1351:258;;-1:-1;;;;;1486:6;1483:30;1480:2;;;1526:1;1523;1516:12;1480:2;-1:-1;1599:4;1570;1547:17;;;;-1:-1;;1543:33;1589:15;;1417:192;1616:128;-1:-1;;;;;1685:54;;1668:76;1752:268;1817:1;1824:101;1838:6;1835:1;1832:13;1824:101;;;1905:11;;;1899:18;1886:11;;;1879:39;1860:2;1853:10;1824:101;;;1940:6;1937:1;1934:13;1931:2;;;2005:1;1996:6;1991:3;1987:16;1980:27;1931:2;1801:219;;;;;;898:7576:11;;;;;;" + "object": "0x60806040523480156200001157600080fd5b5060405162001d3a38038062001d3a833981018060405262000037919081019062000186565b60008054600160a060020a031916600160a060020a03841617905580516200006790600190602084019062000070565b5050506200026b565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620000b357805160ff1916838001178555620000e3565b82800160010185558215620000e3579182015b82811115620000e3578251825591602001919060010190620000c6565b50620000f1929150620000f5565b5090565b6200011291905b80821115620000f15760008155600101620000fc565b90565b60006200012382516200022c565b9392505050565b6000601f820183136200013c57600080fd5b8151620001536200014d8262000204565b620001dd565b915080825260208301602083018583830111156200017057600080fd5b6200017d83828462000238565b50505092915050565b600080604083850312156200019a57600080fd5b6000620001a8858562000115565b92505060208301516001604060020a03811115620001c557600080fd5b620001d3858286016200012a565b9150509250929050565b6040518181016001604060020a0381118282101715620001fc57600080fd5b604052919050565b60006001604060020a038211156200021b57600080fd5b506020601f91909101601f19160190565b600160a060020a031690565b60005b83811015620002555781810151838201526020016200023b565b8381111562000265576000848401525b50505050565b611abf806200027b6000396000f3006080604052600436106100825763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166304ad1e5381146100875780632cd0fc73146100be5780634b95de13146100ec578063690d31141461011a578063b698846314610147578063c6b7f4ee14610174578063f241ffb0146101a2575b600080fd5b34801561009357600080fd5b506100a76100a23660046112d3565b6101cf565b6040516100b59291906118dc565b60405180910390f35b3480156100ca57600080fd5b506100de6100d936600461118b565b61029c565b6040516100b5929190611926565b3480156100f857600080fd5b5061010c610107366004611238565b6107cd565b6040516100b5929190611822565b34801561012657600080fd5b5061013a610135366004611238565b6108a4565b6040516100b59190611858565b34801561015357600080fd5b506101676101623660046111d3565b61095e565b6040516100b591906117f9565b34801561018057600080fd5b5061019461018f366004611139565b6109a9565b6040516100b5929190611869565b3480156101ae57600080fd5b506101c26101bd3660046112d3565b610a86565b6040516100b59190611909565b6101d7610cd0565b6101df610cf0565b6000546040517fc75e0a8100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063c75e0a81906102359087906004016118f8565b606060405180830381600087803b15801561024f57600080fd5b505af1158015610263573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061028791908101906112b5565b91506102938484610a86565b90509250929050565b6000808080808080806102b5898263ffffffff610ba416565b95506102c889601063ffffffff610c1116565b6000546040517f6070410800000000000000000000000000000000000000000000000000000000815291965073ffffffffffffffffffffffffffffffffffffffff169063607041089061031f90899060040161188e565b602060405180830381600087803b15801561033957600080fd5b505af115801561034d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506103719190810190611113565b604080517f4552433230546f6b656e28616464726573732900000000000000000000000000815290519081900360130190209094507fffffffff0000000000000000000000000000000000000000000000000000000087811691161415610526576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8616906370a0823190610424908d906004016117f9565b602060405180830381600087803b15801561043e57600080fd5b505af1158015610452573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610476919081019061131a565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815290985073ffffffffffffffffffffffffffffffffffffffff86169063dd62ed3e906104cd908d908890600401611807565b602060405180830381600087803b1580156104e757600080fd5b505af11580156104fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061051f919081019061131a565b96506107c0565b604080517f455243373231546f6b656e28616464726573732c75696e7432353629000000008152905190819003601c0190207fffffffff00000000000000000000000000000000000000000000000000000000878116911614156107855761059589602463ffffffff610c7216565b92506105a1858461095e565b91508173ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff16146105dd5760006105e0565b60015b60ff1697508473ffffffffffffffffffffffffffffffffffffffff1663e985e9c58b866040518363ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040161063c929190611807565b602060405180830381600087803b15801561065657600080fd5b505af115801561066a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061068e9190810190611297565b8061076a57508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1663081812fc856040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016107009190611918565b602060405180830381600087803b15801561071a57600080fd5b505af115801561072e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506107529190810190611113565b73ffffffffffffffffffffffffffffffffffffffff16145b90508061077857600061077b565b60015b60ff1696506107c0565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b7906118ac565b60405180910390fd5b5050505050509250929050565b6000546040517f7e9d74dc000000000000000000000000000000000000000000000000000000008152606091829173ffffffffffffffffffffffffffffffffffffffff90911690637e9d74dc90610828908790600401611847565b600060405180830381600087803b15801561084257600080fd5b505af1158015610856573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261089c9190810190611203565b915061029384845b606060006060600085519250826040519080825280602002602001820160405280156108ea57816020015b6108d7610cf0565b8152602001906001900390816108cf5790505b509150600090505b80831461095157610931868281518110151561090a57fe5b90602001906020020151868381518110151561092257fe5b90602001906020020151610a86565b828281518110151561093f57fe5b602090810290910101526001016108f2565b8193505b50505092915050565b60006040517f6352211e000000000000000000000000000000000000000000000000000000008152826004820152602081602483875afa80156109a057815192505b50505b92915050565b6060806000606080600086519350836040519080825280602002602001820160405280156109e1578160200160208202803883390190505b50925083604051908082528060200260200182016040528015610a0e578160200160208202803883390190505b509150600090505b808414610a7957610a3e888883815181101515610a2f57fe5b9060200190602002015161029c565b8483815181101515610a4c57fe5b9060200190602002018484815181101515610a6357fe5b6020908102909101019190915252600101610a16565b5090969095509350505050565b610a8e610cf0565b6060610aa3846000015185610140015161029c565b60208401528252610160840151610abb90849061029c565b60608401526040808401919091526001805482516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101008688161502019094169390930492830181900481028201810190945281815292830182828015610b6a5780601f10610b3f57610100808354040283529160200191610b6a565b820191906000526020600020905b815481529060010190602001808311610b4d57829003601f168201915b50505050509050610b7f84600001518261029c565b60a08401526080830152610b93838261029c565b60e084015260c08301525092915050565b600081600401835110151515610be6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b7906118cc565b5050602001517fffffffff000000000000000000000000000000000000000000000000000000001690565b600081601401835110151515610c53576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b7906118bc565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b6000610c7e8383610c85565b9392505050565b600081602001835110151515610cc7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b79061189c565b50016020015190565b604080516060810182526000808252602082018190529181019190915290565b6101006040519081016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6000610c7e82356119d9565b6000610c7e82516119d9565b6000601f82018313610d5f57600080fd5b8135610d72610d6d82611968565b611941565b91508181835260208401935060208101905083856020840282011115610d9757600080fd5b60005b83811015610dc35781610dad8882610d36565b8452506020928301929190910190600101610d9a565b5050505092915050565b6000601f82018313610dde57600080fd5b8135610dec610d6d82611968565b81815260209384019390925082018360005b83811015610dc35781358601610e148882610f11565b8452506020928301929190910190600101610dfe565b6000601f82018313610e3b57600080fd5b8151610e49610d6d82611968565b91508181835260208401935060208101905083856060840282011115610e6e57600080fd5b60005b83811015610dc35781610e848882610f57565b84525060209092019160609190910190600101610e71565b6000601f82018313610ead57600080fd5b8135610ebb610d6d82611968565b81815260209384019390925082018360005b83811015610dc35781358601610ee38882610fb2565b8452506020928301929190910190600101610ecd565b6000610c7e8251611a20565b6000610c7e82516119f2565b6000601f82018313610f2257600080fd5b8135610f30610d6d82611989565b91508082526020830160208301858383011115610f4c57600080fd5b610955838284611a25565b600060608284031215610f6957600080fd5b610f736060611941565b90506000610f818484611107565b8252506020610f9284848301610f05565b6020830152506040610fa684828501610f05565b60408301525092915050565b60006101808284031215610fc557600080fd5b610fd0610180611941565b90506000610fde8484610d36565b8252506020610fef84848301610d36565b602083015250604061100384828501610d36565b604083015250606061101784828501610d36565b606083015250608061102b848285016110fb565b60808301525060a061103f848285016110fb565b60a08301525060c0611053848285016110fb565b60c08301525060e0611067848285016110fb565b60e08301525061010061107c848285016110fb565b61010083015250610120611092848285016110fb565b6101208301525061014082013567ffffffffffffffff8111156110b457600080fd5b6110c084828501610f11565b6101408301525061016082013567ffffffffffffffff8111156110e257600080fd5b6110ee84828501610f11565b6101608301525092915050565b6000610c7e82356119f2565b6000610c7e8251611a1a565b60006020828403121561112557600080fd5b60006111318484610d42565b949350505050565b6000806040838503121561114c57600080fd5b60006111588585610d36565b925050602083013567ffffffffffffffff81111561117557600080fd5b61118185828601610dcd565b9150509250929050565b6000806040838503121561119e57600080fd5b60006111aa8585610d36565b925050602083013567ffffffffffffffff8111156111c757600080fd5b61118185828601610f11565b600080604083850312156111e657600080fd5b60006111f28585610d36565b9250506020611181858286016110fb565b60006020828403121561121557600080fd5b815167ffffffffffffffff81111561122c57600080fd5b61113184828501610e2a565b6000806040838503121561124b57600080fd5b823567ffffffffffffffff81111561126257600080fd5b61126e85828601610e9c565b925050602083013567ffffffffffffffff81111561128b57600080fd5b61118185828601610d4e565b6000602082840312156112a957600080fd5b60006111318484610ef9565b6000606082840312156112c757600080fd5b60006111318484610f57565b600080604083850312156112e657600080fd5b823567ffffffffffffffff8111156112fd57600080fd5b61130985828601610fb2565b925050602061118185828601610d36565b60006020828403121561132c57600080fd5b60006111318484610f05565b611341816119d9565b82525050565b6000611352826119d5565b808452602084019350611364836119cf565b60005b828110156113945761137a868351611619565b611383826119cf565b606096909601959150600101611367565b5093949350505050565b60006113a9826119d5565b808452602084019350836020820285016113c2856119cf565b60005b848110156113f95783830388526113dd838351611656565b92506113e8826119cf565b6020989098019791506001016113c5565b50909695505050505050565b6000611410826119d5565b808452602084019350611422836119cf565b60005b8281101561139457611438868351611759565b611441826119cf565b61010096909601959150600101611425565b600061145e826119d5565b808452602084019350611470836119cf565b60005b82811015611394576114868683516114a0565b61148f826119cf565b602096909601959150600101611473565b611341816119f2565b611341816119f5565b60006114bd826119d5565b8084526114d1816020860160208601611a31565b6114da81611a5d565b9093016020019392505050565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601781527f554e535550504f525445445f41535345545f50524f5859000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b602581527f475245415445525f4f525f455155414c5f544f5f345f4c454e4754485f52455160208201527f5549524544000000000000000000000000000000000000000000000000000000604082015260600190565b8051606083019061162a84826117f0565b50602082015161163d60208501826114a0565b50604082015161165060408501826114a0565b50505050565b805160009061018084019061166b8582611338565b50602083015161167e6020860182611338565b5060408301516116916040860182611338565b5060608301516116a46060860182611338565b5060808301516116b760808601826114a0565b5060a08301516116ca60a08601826114a0565b5060c08301516116dd60c08601826114a0565b5060e08301516116f060e08601826114a0565b506101008301516117056101008601826114a0565b5061012083015161171a6101208601826114a0565b5061014083015184820361014086015261173482826114b2565b91505061016083015184820361016086015261175082826114b2565b95945050505050565b805161010083019061176b84826114a0565b50602082015161177e60208501826114a0565b50604082015161179160408501826114a0565b5060608201516117a460608501826114a0565b5060808201516117b760808501826114a0565b5060a08201516117ca60a08501826114a0565b5060c08201516117dd60c08501826114a0565b5060e082015161165060e08501826114a0565b61134181611a1a565b602081016109a38284611338565b604081016118158285611338565b610c7e6020830184611338565b604080825281016118338185611347565b905081810360208301526111318184611405565b60208082528101610c7e818461139e565b60208082528101610c7e8184611405565b6040808252810161187a8185611453565b905081810360208301526111318184611453565b602081016109a382846114a9565b602080825281016109a3816114e7565b602080825281016109a38161153d565b602080825281016109a38161156d565b602080825281016109a3816115c3565b61016081016118eb8285611619565b610c7e6060830184611759565b60208082528101610c7e8184611656565b61010081016109a38284611759565b602081016109a382846114a0565b6040810161193482856114a0565b610c7e60208301846114a0565b60405181810167ffffffffffffffff8111828210171561196057600080fd5b604052919050565b600067ffffffffffffffff82111561197f57600080fd5b5060209081020190565b600067ffffffffffffffff8211156119a057600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b60200190565b5190565b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b60ff1690565b151590565b82818337506000910152565b60005b83811015611a4c578181015183820152602001611a34565b838111156116505750506000910152565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016905600a265627a7a72305820d3d495bc287663ed8cb2a513cfad47804ffa6e4ad0cfa6d0d0eadf1f907483586c6578706572696d656e74616cf50037", + "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH3 0x11 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD PUSH3 0x1D3A CODESIZE SUB DUP1 PUSH3 0x1D3A DUP4 CODECOPY DUP2 ADD DUP1 PUSH1 0x40 MSTORE PUSH3 0x37 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH3 0x186 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP5 AND OR SWAP1 SSTORE DUP1 MLOAD PUSH3 0x67 SWAP1 PUSH1 0x1 SWAP1 PUSH1 0x20 DUP5 ADD SWAP1 PUSH3 0x70 JUMP JUMPDEST POP POP POP PUSH3 0x26B JUMP JUMPDEST DUP3 DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 PUSH1 0x1F ADD PUSH1 0x20 SWAP1 DIV DUP2 ADD SWAP3 DUP3 PUSH1 0x1F LT PUSH3 0xB3 JUMPI DUP1 MLOAD PUSH1 0xFF NOT AND DUP4 DUP1 ADD OR DUP6 SSTORE PUSH3 0xE3 JUMP JUMPDEST DUP3 DUP1 ADD PUSH1 0x1 ADD DUP6 SSTORE DUP3 ISZERO PUSH3 0xE3 JUMPI SWAP2 DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH3 0xE3 JUMPI DUP3 MLOAD DUP3 SSTORE SWAP2 PUSH1 0x20 ADD SWAP2 SWAP1 PUSH1 0x1 ADD SWAP1 PUSH3 0xC6 JUMP JUMPDEST POP PUSH3 0xF1 SWAP3 SWAP2 POP PUSH3 0xF5 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH3 0x112 SWAP2 SWAP1 JUMPDEST DUP1 DUP3 GT ISZERO PUSH3 0xF1 JUMPI PUSH1 0x0 DUP2 SSTORE PUSH1 0x1 ADD PUSH3 0xFC JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH3 0x123 DUP3 MLOAD PUSH3 0x22C JUMP JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH3 0x13C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 MLOAD PUSH3 0x153 PUSH3 0x14D DUP3 PUSH3 0x204 JUMP JUMPDEST PUSH3 0x1DD JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH3 0x170 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH3 0x17D DUP4 DUP3 DUP5 PUSH3 0x238 JUMP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH3 0x19A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH3 0x1A8 DUP6 DUP6 PUSH3 0x115 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 DUP4 ADD MLOAD PUSH1 0x1 PUSH1 0x40 PUSH1 0x2 EXP SUB DUP2 GT ISZERO PUSH3 0x1C5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH3 0x1D3 DUP6 DUP3 DUP7 ADD PUSH3 0x12A JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH1 0x1 PUSH1 0x40 PUSH1 0x2 EXP SUB DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH3 0x1FC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1 PUSH1 0x40 PUSH1 0x2 EXP SUB DUP3 GT ISZERO PUSH3 0x21B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH1 0x1F NOT AND ADD SWAP1 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP1 JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH3 0x255 JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH3 0x23B JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH3 0x265 JUMPI PUSH1 0x0 DUP5 DUP5 ADD MSTORE JUMPDEST POP POP POP POP JUMP JUMPDEST PUSH2 0x1ABF DUP1 PUSH3 0x27B PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x82 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x4AD1E53 DUP2 EQ PUSH2 0x87 JUMPI DUP1 PUSH4 0x2CD0FC73 EQ PUSH2 0xBE JUMPI DUP1 PUSH4 0x4B95DE13 EQ PUSH2 0xEC JUMPI DUP1 PUSH4 0x690D3114 EQ PUSH2 0x11A JUMPI DUP1 PUSH4 0xB6988463 EQ PUSH2 0x147 JUMPI DUP1 PUSH4 0xC6B7F4EE EQ PUSH2 0x174 JUMPI DUP1 PUSH4 0xF241FFB0 EQ PUSH2 0x1A2 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x93 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xA7 PUSH2 0xA2 CALLDATASIZE PUSH1 0x4 PUSH2 0x12D3 JUMP JUMPDEST PUSH2 0x1CF JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xB5 SWAP3 SWAP2 SWAP1 PUSH2 0x18DC JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xCA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xDE PUSH2 0xD9 CALLDATASIZE PUSH1 0x4 PUSH2 0x118B JUMP JUMPDEST PUSH2 0x29C JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xB5 SWAP3 SWAP2 SWAP1 PUSH2 0x1926 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xF8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x10C PUSH2 0x107 CALLDATASIZE PUSH1 0x4 PUSH2 0x1238 JUMP JUMPDEST PUSH2 0x7CD JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xB5 SWAP3 SWAP2 SWAP1 PUSH2 0x1822 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x126 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x13A PUSH2 0x135 CALLDATASIZE PUSH1 0x4 PUSH2 0x1238 JUMP JUMPDEST PUSH2 0x8A4 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xB5 SWAP2 SWAP1 PUSH2 0x1858 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x153 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x167 PUSH2 0x162 CALLDATASIZE PUSH1 0x4 PUSH2 0x11D3 JUMP JUMPDEST PUSH2 0x95E JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xB5 SWAP2 SWAP1 PUSH2 0x17F9 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x180 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x194 PUSH2 0x18F CALLDATASIZE PUSH1 0x4 PUSH2 0x1139 JUMP JUMPDEST PUSH2 0x9A9 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xB5 SWAP3 SWAP2 SWAP1 PUSH2 0x1869 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1AE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1C2 PUSH2 0x1BD CALLDATASIZE PUSH1 0x4 PUSH2 0x12D3 JUMP JUMPDEST PUSH2 0xA86 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xB5 SWAP2 SWAP1 PUSH2 0x1909 JUMP JUMPDEST PUSH2 0x1D7 PUSH2 0xCD0 JUMP JUMPDEST PUSH2 0x1DF PUSH2 0xCF0 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x40 MLOAD PUSH32 0xC75E0A8100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP2 AND SWAP1 PUSH4 0xC75E0A81 SWAP1 PUSH2 0x235 SWAP1 DUP8 SWAP1 PUSH1 0x4 ADD PUSH2 0x18F8 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x24F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x263 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x287 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x12B5 JUMP JUMPDEST SWAP2 POP PUSH2 0x293 DUP5 DUP5 PUSH2 0xA86 JUMP JUMPDEST SWAP1 POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP1 DUP1 DUP1 DUP1 DUP1 DUP1 PUSH2 0x2B5 DUP10 DUP3 PUSH4 0xFFFFFFFF PUSH2 0xBA4 AND JUMP JUMPDEST SWAP6 POP PUSH2 0x2C8 DUP10 PUSH1 0x10 PUSH4 0xFFFFFFFF PUSH2 0xC11 AND JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x40 MLOAD PUSH32 0x6070410800000000000000000000000000000000000000000000000000000000 DUP2 MSTORE SWAP2 SWAP7 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 PUSH4 0x60704108 SWAP1 PUSH2 0x31F SWAP1 DUP10 SWAP1 PUSH1 0x4 ADD PUSH2 0x188E JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x339 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x34D JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x371 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x1113 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x4552433230546F6B656E28616464726573732900000000000000000000000000 DUP2 MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x13 ADD SWAP1 KECCAK256 SWAP1 SWAP5 POP PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP8 DUP2 AND SWAP2 AND EQ ISZERO PUSH2 0x526 JUMPI PUSH1 0x40 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND SWAP1 PUSH4 0x70A08231 SWAP1 PUSH2 0x424 SWAP1 DUP14 SWAP1 PUSH1 0x4 ADD PUSH2 0x17F9 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x43E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x452 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x476 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x131A JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0xDD62ED3E00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE SWAP1 SWAP9 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND SWAP1 PUSH4 0xDD62ED3E SWAP1 PUSH2 0x4CD SWAP1 DUP14 SWAP1 DUP9 SWAP1 PUSH1 0x4 ADD PUSH2 0x1807 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x4E7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x4FB JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x51F SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x131A JUMP JUMPDEST SWAP7 POP PUSH2 0x7C0 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x455243373231546F6B656E28616464726573732C75696E743235362900000000 DUP2 MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x1C ADD SWAP1 KECCAK256 PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP8 DUP2 AND SWAP2 AND EQ ISZERO PUSH2 0x785 JUMPI PUSH2 0x595 DUP10 PUSH1 0x24 PUSH4 0xFFFFFFFF PUSH2 0xC72 AND JUMP JUMPDEST SWAP3 POP PUSH2 0x5A1 DUP6 DUP5 PUSH2 0x95E JUMP JUMPDEST SWAP2 POP DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP11 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ PUSH2 0x5DD JUMPI PUSH1 0x0 PUSH2 0x5E0 JUMP JUMPDEST PUSH1 0x1 JUMPDEST PUSH1 0xFF AND SWAP8 POP DUP5 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0xE985E9C5 DUP12 DUP7 PUSH1 0x40 MLOAD DUP4 PUSH4 0xFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x63C SWAP3 SWAP2 SWAP1 PUSH2 0x1807 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x656 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x66A JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x68E SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x1297 JUMP JUMPDEST DUP1 PUSH2 0x76A JUMPI POP DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0x81812FC DUP6 PUSH1 0x40 MLOAD DUP3 PUSH4 0xFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x700 SWAP2 SWAP1 PUSH2 0x1918 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x71A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x72E JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x752 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x1113 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ JUMPDEST SWAP1 POP DUP1 PUSH2 0x778 JUMPI PUSH1 0x0 PUSH2 0x77B JUMP JUMPDEST PUSH1 0x1 JUMPDEST PUSH1 0xFF AND SWAP7 POP PUSH2 0x7C0 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x7B7 SWAP1 PUSH2 0x18AC JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST POP POP POP POP POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x40 MLOAD PUSH32 0x7E9D74DC00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x60 SWAP2 DUP3 SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP2 AND SWAP1 PUSH4 0x7E9D74DC SWAP1 PUSH2 0x828 SWAP1 DUP8 SWAP1 PUSH1 0x4 ADD PUSH2 0x1847 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x842 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x856 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x0 DUP3 RETURNDATACOPY PUSH1 0x1F RETURNDATASIZE SWAP1 DUP2 ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND DUP3 ADD PUSH1 0x40 MSTORE PUSH2 0x89C SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x1203 JUMP JUMPDEST SWAP2 POP PUSH2 0x293 DUP5 DUP5 JUMPDEST PUSH1 0x60 PUSH1 0x0 PUSH1 0x60 PUSH1 0x0 DUP6 MLOAD SWAP3 POP DUP3 PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0x8EA JUMPI DUP2 PUSH1 0x20 ADD JUMPDEST PUSH2 0x8D7 PUSH2 0xCF0 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD SWAP1 PUSH1 0x1 SWAP1 SUB SWAP1 DUP2 PUSH2 0x8CF JUMPI SWAP1 POP JUMPDEST POP SWAP2 POP PUSH1 0x0 SWAP1 POP JUMPDEST DUP1 DUP4 EQ PUSH2 0x951 JUMPI PUSH2 0x931 DUP7 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x90A JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP7 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x922 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0xA86 JUMP JUMPDEST DUP3 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x93F JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MSTORE PUSH1 0x1 ADD PUSH2 0x8F2 JUMP JUMPDEST DUP2 SWAP4 POP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x40 MLOAD PUSH32 0x6352211E00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE DUP3 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x20 DUP2 PUSH1 0x24 DUP4 DUP8 GAS STATICCALL DUP1 ISZERO PUSH2 0x9A0 JUMPI DUP2 MLOAD SWAP3 POP JUMPDEST POP POP JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x60 DUP1 PUSH1 0x0 PUSH1 0x60 DUP1 PUSH1 0x0 DUP7 MLOAD SWAP4 POP DUP4 PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0x9E1 JUMPI DUP2 PUSH1 0x20 ADD PUSH1 0x20 DUP3 MUL DUP1 CODESIZE DUP4 CODECOPY ADD SWAP1 POP JUMPDEST POP SWAP3 POP DUP4 PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0xA0E JUMPI DUP2 PUSH1 0x20 ADD PUSH1 0x20 DUP3 MUL DUP1 CODESIZE DUP4 CODECOPY ADD SWAP1 POP JUMPDEST POP SWAP2 POP PUSH1 0x0 SWAP1 POP JUMPDEST DUP1 DUP5 EQ PUSH2 0xA79 JUMPI PUSH2 0xA3E DUP9 DUP9 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xA2F JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x29C JUMP JUMPDEST DUP5 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xA4C JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD DUP5 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xA63 JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD SWAP2 SWAP1 SWAP2 MSTORE MSTORE PUSH1 0x1 ADD PUSH2 0xA16 JUMP JUMPDEST POP SWAP1 SWAP7 SWAP1 SWAP6 POP SWAP4 POP POP POP POP JUMP JUMPDEST PUSH2 0xA8E PUSH2 0xCF0 JUMP JUMPDEST PUSH1 0x60 PUSH2 0xAA3 DUP5 PUSH1 0x0 ADD MLOAD DUP6 PUSH2 0x140 ADD MLOAD PUSH2 0x29C JUMP JUMPDEST PUSH1 0x20 DUP5 ADD MSTORE DUP3 MSTORE PUSH2 0x160 DUP5 ADD MLOAD PUSH2 0xABB SWAP1 DUP5 SWAP1 PUSH2 0x29C JUMP JUMPDEST PUSH1 0x60 DUP5 ADD MSTORE PUSH1 0x40 DUP1 DUP5 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x1 DUP1 SLOAD DUP3 MLOAD PUSH1 0x20 PUSH1 0x1F PUSH1 0x2 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH2 0x100 DUP7 DUP9 AND ISZERO MUL ADD SWAP1 SWAP5 AND SWAP4 SWAP1 SWAP4 DIV SWAP3 DUP4 ADD DUP2 SWAP1 DIV DUP2 MUL DUP3 ADD DUP2 ADD SWAP1 SWAP5 MSTORE DUP2 DUP2 MSTORE SWAP3 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0xB6A JUMPI DUP1 PUSH1 0x1F LT PUSH2 0xB3F JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0xB6A JUMP JUMPDEST DUP3 ADD SWAP2 SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD DUP2 MSTORE SWAP1 PUSH1 0x1 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0xB4D JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP POP POP SWAP1 POP PUSH2 0xB7F DUP5 PUSH1 0x0 ADD MLOAD DUP3 PUSH2 0x29C JUMP JUMPDEST PUSH1 0xA0 DUP5 ADD MSTORE PUSH1 0x80 DUP4 ADD MSTORE PUSH2 0xB93 DUP4 DUP3 PUSH2 0x29C JUMP JUMPDEST PUSH1 0xE0 DUP5 ADD MSTORE PUSH1 0xC0 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x4 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0xBE6 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x7B7 SWAP1 PUSH2 0x18CC JUMP JUMPDEST POP POP PUSH1 0x20 ADD MLOAD PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x14 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0xC53 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x7B7 SWAP1 PUSH2 0x18BC JUMP JUMPDEST POP ADD PUSH1 0x14 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC7E DUP4 DUP4 PUSH2 0xC85 JUMP JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x20 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0xCC7 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x7B7 SWAP1 PUSH2 0x189C JUMP JUMPDEST POP ADD PUSH1 0x20 ADD MLOAD SWAP1 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x60 DUP2 ADD DUP3 MSTORE PUSH1 0x0 DUP1 DUP3 MSTORE PUSH1 0x20 DUP3 ADD DUP2 SWAP1 MSTORE SWAP2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE SWAP1 JUMP JUMPDEST PUSH2 0x100 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC7E DUP3 CALLDATALOAD PUSH2 0x19D9 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC7E DUP3 MLOAD PUSH2 0x19D9 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xD5F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xD72 PUSH2 0xD6D DUP3 PUSH2 0x1968 JUMP JUMPDEST PUSH2 0x1941 JUMP JUMPDEST SWAP2 POP DUP2 DUP2 DUP4 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH1 0x20 DUP2 ADD SWAP1 POP DUP4 DUP6 PUSH1 0x20 DUP5 MUL DUP3 ADD GT ISZERO PUSH2 0xD97 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xDC3 JUMPI DUP2 PUSH2 0xDAD DUP9 DUP3 PUSH2 0xD36 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0xD9A JUMP JUMPDEST POP POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xDDE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xDEC PUSH2 0xD6D DUP3 PUSH2 0x1968 JUMP JUMPDEST DUP2 DUP2 MSTORE PUSH1 0x20 SWAP4 DUP5 ADD SWAP4 SWAP1 SWAP3 POP DUP3 ADD DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xDC3 JUMPI DUP2 CALLDATALOAD DUP7 ADD PUSH2 0xE14 DUP9 DUP3 PUSH2 0xF11 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0xDFE JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xE3B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 MLOAD PUSH2 0xE49 PUSH2 0xD6D DUP3 PUSH2 0x1968 JUMP JUMPDEST SWAP2 POP DUP2 DUP2 DUP4 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH1 0x20 DUP2 ADD SWAP1 POP DUP4 DUP6 PUSH1 0x60 DUP5 MUL DUP3 ADD GT ISZERO PUSH2 0xE6E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xDC3 JUMPI DUP2 PUSH2 0xE84 DUP9 DUP3 PUSH2 0xF57 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x60 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0xE71 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xEAD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xEBB PUSH2 0xD6D DUP3 PUSH2 0x1968 JUMP JUMPDEST DUP2 DUP2 MSTORE PUSH1 0x20 SWAP4 DUP5 ADD SWAP4 SWAP1 SWAP3 POP DUP3 ADD DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xDC3 JUMPI DUP2 CALLDATALOAD DUP7 ADD PUSH2 0xEE3 DUP9 DUP3 PUSH2 0xFB2 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0xECD JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC7E DUP3 MLOAD PUSH2 0x1A20 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC7E DUP3 MLOAD PUSH2 0x19F2 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xF22 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xF30 PUSH2 0xD6D DUP3 PUSH2 0x1989 JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH2 0xF4C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x955 DUP4 DUP3 DUP5 PUSH2 0x1A25 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x60 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xF69 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xF73 PUSH1 0x60 PUSH2 0x1941 JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0xF81 DUP5 DUP5 PUSH2 0x1107 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0xF92 DUP5 DUP5 DUP4 ADD PUSH2 0xF05 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0xFA6 DUP5 DUP3 DUP6 ADD PUSH2 0xF05 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x180 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xFC5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xFD0 PUSH2 0x180 PUSH2 0x1941 JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0xFDE DUP5 DUP5 PUSH2 0xD36 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0xFEF DUP5 DUP5 DUP4 ADD PUSH2 0xD36 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0x1003 DUP5 DUP3 DUP6 ADD PUSH2 0xD36 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP PUSH1 0x60 PUSH2 0x1017 DUP5 DUP3 DUP6 ADD PUSH2 0xD36 JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MSTORE POP PUSH1 0x80 PUSH2 0x102B DUP5 DUP3 DUP6 ADD PUSH2 0x10FB JUMP JUMPDEST PUSH1 0x80 DUP4 ADD MSTORE POP PUSH1 0xA0 PUSH2 0x103F DUP5 DUP3 DUP6 ADD PUSH2 0x10FB JUMP JUMPDEST PUSH1 0xA0 DUP4 ADD MSTORE POP PUSH1 0xC0 PUSH2 0x1053 DUP5 DUP3 DUP6 ADD PUSH2 0x10FB JUMP JUMPDEST PUSH1 0xC0 DUP4 ADD MSTORE POP PUSH1 0xE0 PUSH2 0x1067 DUP5 DUP3 DUP6 ADD PUSH2 0x10FB JUMP JUMPDEST PUSH1 0xE0 DUP4 ADD MSTORE POP PUSH2 0x100 PUSH2 0x107C DUP5 DUP3 DUP6 ADD PUSH2 0x10FB JUMP JUMPDEST PUSH2 0x100 DUP4 ADD MSTORE POP PUSH2 0x120 PUSH2 0x1092 DUP5 DUP3 DUP6 ADD PUSH2 0x10FB JUMP JUMPDEST PUSH2 0x120 DUP4 ADD MSTORE POP PUSH2 0x140 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x10B4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x10C0 DUP5 DUP3 DUP6 ADD PUSH2 0xF11 JUMP JUMPDEST PUSH2 0x140 DUP4 ADD MSTORE POP PUSH2 0x160 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x10E2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x10EE DUP5 DUP3 DUP6 ADD PUSH2 0xF11 JUMP JUMPDEST PUSH2 0x160 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC7E DUP3 CALLDATALOAD PUSH2 0x19F2 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC7E DUP3 MLOAD PUSH2 0x1A1A JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x1125 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x1131 DUP5 DUP5 PUSH2 0xD42 JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x114C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x1158 DUP6 DUP6 PUSH2 0xD36 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 DUP4 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x1175 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x1181 DUP6 DUP3 DUP7 ADD PUSH2 0xDCD JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x119E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x11AA DUP6 DUP6 PUSH2 0xD36 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 DUP4 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x11C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x1181 DUP6 DUP3 DUP7 ADD PUSH2 0xF11 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x11E6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x11F2 DUP6 DUP6 PUSH2 0xD36 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x1181 DUP6 DUP3 DUP7 ADD PUSH2 0x10FB JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x1215 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 MLOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x122C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x1131 DUP5 DUP3 DUP6 ADD PUSH2 0xE2A JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x124B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP3 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x1262 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x126E DUP6 DUP3 DUP7 ADD PUSH2 0xE9C JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 DUP4 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x128B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x1181 DUP6 DUP3 DUP7 ADD PUSH2 0xD4E JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x12A9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x1131 DUP5 DUP5 PUSH2 0xEF9 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x60 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x12C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x1131 DUP5 DUP5 PUSH2 0xF57 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x12E6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP3 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x12FD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x1309 DUP6 DUP3 DUP7 ADD PUSH2 0xFB2 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x1181 DUP6 DUP3 DUP7 ADD PUSH2 0xD36 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x132C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x1131 DUP5 DUP5 PUSH2 0xF05 JUMP JUMPDEST PUSH2 0x1341 DUP2 PUSH2 0x19D9 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x1352 DUP3 PUSH2 0x19D5 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH2 0x1364 DUP4 PUSH2 0x19CF JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x1394 JUMPI PUSH2 0x137A DUP7 DUP4 MLOAD PUSH2 0x1619 JUMP JUMPDEST PUSH2 0x1383 DUP3 PUSH2 0x19CF JUMP JUMPDEST PUSH1 0x60 SWAP7 SWAP1 SWAP7 ADD SWAP6 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x1367 JUMP JUMPDEST POP SWAP4 SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x13A9 DUP3 PUSH2 0x19D5 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP DUP4 PUSH1 0x20 DUP3 MUL DUP6 ADD PUSH2 0x13C2 DUP6 PUSH2 0x19CF JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP5 DUP2 LT ISZERO PUSH2 0x13F9 JUMPI DUP4 DUP4 SUB DUP9 MSTORE PUSH2 0x13DD DUP4 DUP4 MLOAD PUSH2 0x1656 JUMP JUMPDEST SWAP3 POP PUSH2 0x13E8 DUP3 PUSH2 0x19CF JUMP JUMPDEST PUSH1 0x20 SWAP9 SWAP1 SWAP9 ADD SWAP8 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x13C5 JUMP JUMPDEST POP SWAP1 SWAP7 SWAP6 POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x1410 DUP3 PUSH2 0x19D5 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH2 0x1422 DUP4 PUSH2 0x19CF JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x1394 JUMPI PUSH2 0x1438 DUP7 DUP4 MLOAD PUSH2 0x1759 JUMP JUMPDEST PUSH2 0x1441 DUP3 PUSH2 0x19CF JUMP JUMPDEST PUSH2 0x100 SWAP7 SWAP1 SWAP7 ADD SWAP6 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x1425 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x145E DUP3 PUSH2 0x19D5 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH2 0x1470 DUP4 PUSH2 0x19CF JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x1394 JUMPI PUSH2 0x1486 DUP7 DUP4 MLOAD PUSH2 0x14A0 JUMP JUMPDEST PUSH2 0x148F DUP3 PUSH2 0x19CF JUMP JUMPDEST PUSH1 0x20 SWAP7 SWAP1 SWAP7 ADD SWAP6 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x1473 JUMP JUMPDEST PUSH2 0x1341 DUP2 PUSH2 0x19F2 JUMP JUMPDEST PUSH2 0x1341 DUP2 PUSH2 0x19F5 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x14BD DUP3 PUSH2 0x19D5 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH2 0x14D1 DUP2 PUSH1 0x20 DUP7 ADD PUSH1 0x20 DUP7 ADD PUSH2 0x1A31 JUMP JUMPDEST PUSH2 0x14DA DUP2 PUSH2 0x1A5D JUMP JUMPDEST SWAP1 SWAP4 ADD PUSH1 0x20 ADD SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F33325F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x17 DUP2 MSTORE PUSH32 0x554E535550504F525445445F41535345545F50524F5859000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F32305F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x25 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F345F4C454E4754485F524551 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5549524544000000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST DUP1 MLOAD PUSH1 0x60 DUP4 ADD SWAP1 PUSH2 0x162A DUP5 DUP3 PUSH2 0x17F0 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x163D PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x1650 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP POP POP POP JUMP JUMPDEST DUP1 MLOAD PUSH1 0x0 SWAP1 PUSH2 0x180 DUP5 ADD SWAP1 PUSH2 0x166B DUP6 DUP3 PUSH2 0x1338 JUMP JUMPDEST POP PUSH1 0x20 DUP4 ADD MLOAD PUSH2 0x167E PUSH1 0x20 DUP7 ADD DUP3 PUSH2 0x1338 JUMP JUMPDEST POP PUSH1 0x40 DUP4 ADD MLOAD PUSH2 0x1691 PUSH1 0x40 DUP7 ADD DUP3 PUSH2 0x1338 JUMP JUMPDEST POP PUSH1 0x60 DUP4 ADD MLOAD PUSH2 0x16A4 PUSH1 0x60 DUP7 ADD DUP3 PUSH2 0x1338 JUMP JUMPDEST POP PUSH1 0x80 DUP4 ADD MLOAD PUSH2 0x16B7 PUSH1 0x80 DUP7 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0xA0 DUP4 ADD MLOAD PUSH2 0x16CA PUSH1 0xA0 DUP7 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0xC0 DUP4 ADD MLOAD PUSH2 0x16DD PUSH1 0xC0 DUP7 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0xE0 DUP4 ADD MLOAD PUSH2 0x16F0 PUSH1 0xE0 DUP7 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH2 0x100 DUP4 ADD MLOAD PUSH2 0x1705 PUSH2 0x100 DUP7 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH2 0x120 DUP4 ADD MLOAD PUSH2 0x171A PUSH2 0x120 DUP7 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH2 0x140 DUP4 ADD MLOAD DUP5 DUP3 SUB PUSH2 0x140 DUP7 ADD MSTORE PUSH2 0x1734 DUP3 DUP3 PUSH2 0x14B2 JUMP JUMPDEST SWAP2 POP POP PUSH2 0x160 DUP4 ADD MLOAD DUP5 DUP3 SUB PUSH2 0x160 DUP7 ADD MSTORE PUSH2 0x1750 DUP3 DUP3 PUSH2 0x14B2 JUMP JUMPDEST SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST DUP1 MLOAD PUSH2 0x100 DUP4 ADD SWAP1 PUSH2 0x176B DUP5 DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x177E PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x1791 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0x60 DUP3 ADD MLOAD PUSH2 0x17A4 PUSH1 0x60 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0x80 DUP3 ADD MLOAD PUSH2 0x17B7 PUSH1 0x80 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0xA0 DUP3 ADD MLOAD PUSH2 0x17CA PUSH1 0xA0 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0xC0 DUP3 ADD MLOAD PUSH2 0x17DD PUSH1 0xC0 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0xE0 DUP3 ADD MLOAD PUSH2 0x1650 PUSH1 0xE0 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST PUSH2 0x1341 DUP2 PUSH2 0x1A1A JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x9A3 DUP3 DUP5 PUSH2 0x1338 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x1815 DUP3 DUP6 PUSH2 0x1338 JUMP JUMPDEST PUSH2 0xC7E PUSH1 0x20 DUP4 ADD DUP5 PUSH2 0x1338 JUMP JUMPDEST PUSH1 0x40 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x1833 DUP2 DUP6 PUSH2 0x1347 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0x1131 DUP2 DUP5 PUSH2 0x1405 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xC7E DUP2 DUP5 PUSH2 0x139E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xC7E DUP2 DUP5 PUSH2 0x1405 JUMP JUMPDEST PUSH1 0x40 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x187A DUP2 DUP6 PUSH2 0x1453 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0x1131 DUP2 DUP5 PUSH2 0x1453 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x9A3 DUP3 DUP5 PUSH2 0x14A9 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x9A3 DUP2 PUSH2 0x14E7 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x9A3 DUP2 PUSH2 0x153D JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x9A3 DUP2 PUSH2 0x156D JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x9A3 DUP2 PUSH2 0x15C3 JUMP JUMPDEST PUSH2 0x160 DUP2 ADD PUSH2 0x18EB DUP3 DUP6 PUSH2 0x1619 JUMP JUMPDEST PUSH2 0xC7E PUSH1 0x60 DUP4 ADD DUP5 PUSH2 0x1759 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xC7E DUP2 DUP5 PUSH2 0x1656 JUMP JUMPDEST PUSH2 0x100 DUP2 ADD PUSH2 0x9A3 DUP3 DUP5 PUSH2 0x1759 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x9A3 DUP3 DUP5 PUSH2 0x14A0 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x1934 DUP3 DUP6 PUSH2 0x14A0 JUMP JUMPDEST PUSH2 0xC7E PUSH1 0x20 DUP4 ADD DUP5 PUSH2 0x14A0 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0x1960 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x197F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 SWAP1 DUP2 MUL ADD SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x19A0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND ADD SWAP1 JUMP JUMPDEST PUSH1 0x20 ADD SWAP1 JUMP JUMPDEST MLOAD SWAP1 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND SWAP1 JUMP JUMPDEST PUSH1 0xFF AND SWAP1 JUMP JUMPDEST ISZERO ISZERO SWAP1 JUMP JUMPDEST DUP3 DUP2 DUP4 CALLDATACOPY POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x1A4C JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x1A34 JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0x1650 JUMPI POP POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x1F ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND SWAP1 JUMP STOP LOG2 PUSH6 0x627A7A723058 KECCAK256 0xd3 0xd4 SWAP6 0xbc 0x28 PUSH23 0x63ED8CB2A513CFAD47804FFA6E4AD0CFA6D0D0EADF1F90 PUSH21 0x83586C6578706572696D656E74616CF50037000000 ", + "sourceMap": "898:8536:11:-;;;1916:167;8:9:-1;5:2;;;30:1;27;20:12;5:2;1916:167:11;;;;;;;;;;;;;;;;;;;;;;;;2005:8;:31;;-1:-1:-1;;;;;;2005:31:11;-1:-1:-1;;;;;2005:31:11;;;;;2046:30;;;;-1:-1:-1;;2046:30:11;;;;;:::i;:::-;;1916:167;;898:8536;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;898:8536:11;;;-1:-1:-1;898:8536:11;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::o;5:122:-1:-;;83:39;114:6;108:13;83:39;;;74:48;68:59;-1:-1;;;68:59;135:442;;240:4;228:17;;224:27;-1:-1;214:2;;265:1;262;255:12;214:2;295:6;289:13;317:64;332:48;373:6;332:48;;;317:64;;;308:73;;401:6;394:5;387:21;437:4;429:6;425:17;470:4;463:5;459:16;505:3;496:6;491:3;487:16;484:25;481:2;;;522:1;519;512:12;481:2;532:39;564:6;559:3;554;532:39;;;207:370;;;;;;;;585:496;;;726:2;714:9;705:7;701:23;697:32;694:2;;;742:1;739;732:12;694:2;777:1;794:64;850:7;830:9;794:64;;;784:74;;756:108;916:2;905:9;901:18;895:25;-1:-1;;;;;932:6;929:30;926:2;;;972:1;969;962:12;926:2;992:73;1057:7;1048:6;1037:9;1033:22;992:73;;;982:83;;874:197;688:393;;;;;;1088:256;1150:2;1144:9;1176:17;;;-1:-1;;;;;1236:34;;1272:22;;;1233:62;1230:2;;;1308:1;1305;1298:12;1230:2;1324;1317:22;1128:216;;-1:-1;1128:216;1351:258;;-1:-1;;;;;1486:6;1483:30;1480:2;;;1526:1;1523;1516:12;1480:2;-1:-1;1599:4;1570;1547:17;;;;-1:-1;;1543:33;1589:15;;1417:192;1616:128;-1:-1;;;;;1685:54;;1668:76;1752:268;1817:1;1824:101;1838:6;1835:1;1832:13;1824:101;;;1905:11;;;1899:18;1886:11;;;1879:39;1860:2;1853:10;1824:101;;;1940:6;1937:1;1934:13;1931:2;;;2005:1;1996:6;1991:3;1987:16;1980:27;1931:2;1801:219;;;;;;898:8536:11;;;;;;" }, "deployedBytecode": { "linkReferences": {}, - "object": "0x6080604052600436106100775763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166304ad1e53811461007c5780632cd0fc73146100b35780634b95de13146100e1578063690d31141461010f578063b69884631461013c578063f241ffb014610169575b600080fd5b34801561008857600080fd5b5061009c610097366004611118565b610196565b6040516100aa9291906116af565b60405180910390f35b3480156100bf57600080fd5b506100d36100ce366004610fc6565b610263565b6040516100aa9291906116f9565b3480156100ed57600080fd5b506101016100fc36600461107d565b610794565b6040516100aa92919061161a565b34801561011b57600080fd5b5061012f61012a36600461107d565b61086b565b6040516100aa9190611650565b34801561014857600080fd5b5061015c610157366004611018565b610925565b6040516100aa91906115f1565b34801561017557600080fd5b50610189610184366004611118565b610970565b6040516100aa91906116dc565b61019e610bba565b6101a6610bda565b6000546040517fc75e0a8100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063c75e0a81906101fc9087906004016116cb565b606060405180830381600087803b15801561021657600080fd5b505af115801561022a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061024e91908101906110fa565b915061025a8484610970565b90509250929050565b60008080808080808061027c898263ffffffff610a8e16565b955061028f89601063ffffffff610afb16565b6000546040517f6070410800000000000000000000000000000000000000000000000000000000815291965073ffffffffffffffffffffffffffffffffffffffff16906360704108906102e6908990600401611661565b602060405180830381600087803b15801561030057600080fd5b505af1158015610314573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506103389190810190610fa0565b604080517f4552433230546f6b656e28616464726573732900000000000000000000000000815290519081900360130190209094507fffffffff00000000000000000000000000000000000000000000000000000000878116911614156104ed576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8616906370a08231906103eb908d906004016115f1565b602060405180830381600087803b15801561040557600080fd5b505af1158015610419573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061043d919081019061115f565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815290985073ffffffffffffffffffffffffffffffffffffffff86169063dd62ed3e90610494908d9088906004016115ff565b602060405180830381600087803b1580156104ae57600080fd5b505af11580156104c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506104e6919081019061115f565b9650610787565b604080517f455243373231546f6b656e28616464726573732c75696e7432353629000000008152905190819003601c0190207fffffffff000000000000000000000000000000000000000000000000000000008781169116141561074c5761055c89602463ffffffff610b5c16565b92506105688584610925565b91508173ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff16146105a45760006105a7565b60015b60ff1697508473ffffffffffffffffffffffffffffffffffffffff1663e985e9c58b866040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016106039291906115ff565b602060405180830381600087803b15801561061d57600080fd5b505af1158015610631573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061065591908101906110dc565b8061073157508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1663081812fc856040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016106c791906116eb565b602060405180830381600087803b1580156106e157600080fd5b505af11580156106f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506107199190810190610fa0565b73ffffffffffffffffffffffffffffffffffffffff16145b90508061073f576000610742565b60015b60ff169650610787565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161077e9061167f565b60405180910390fd5b5050505050509250929050565b6000546040517f7e9d74dc000000000000000000000000000000000000000000000000000000008152606091829173ffffffffffffffffffffffffffffffffffffffff90911690637e9d74dc906107ef90879060040161163f565b600060405180830381600087803b15801561080957600080fd5b505af115801561081d573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526108639190810190611048565b915061025a84845b606060006060600085519250826040519080825280602002602001820160405280156108b157816020015b61089e610bda565b8152602001906001900390816108965790505b509150600090505b808314610918576108f886828151811015156108d157fe5b9060200190602002015186838151811015156108e957fe5b90602001906020020151610970565b828281518110151561090657fe5b602090810290910101526001016108b9565b8193505b50505092915050565b60006040517f6352211e000000000000000000000000000000000000000000000000000000008152826004820152602081602483875afa801561096757815192505b50505b92915050565b610978610bda565b606061098d8460000151856101400151610263565b602084015282526101608401516109a5908490610263565b60608401526040808401919091526001805482516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101008688161502019094169390930492830181900481028201810190945281815292830182828015610a545780601f10610a2957610100808354040283529160200191610a54565b820191906000526020600020905b815481529060010190602001808311610a3757829003601f168201915b50505050509050610a69846000015182610263565b60a08401526080830152610a7d8382610263565b60e084015260c08301525092915050565b600081600401835110151515610ad0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161077e9061169f565b5050602001517fffffffff000000000000000000000000000000000000000000000000000000001690565b600081601401835110151515610b3d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161077e9061168f565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b6000610b688383610b6f565b9392505050565b600081602001835110151515610bb1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161077e9061166f565b50016020015190565b604080516060810182526000808252602082018190529181019190915290565b6101006040519081016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6000610b6882356117ac565b6000610b6882516117ac565b6000601f82018313610c4957600080fd5b8135610c5c610c578261173b565b611714565b91508181835260208401935060208101905083856020840282011115610c8157600080fd5b60005b83811015610cad5781610c978882610c20565b8452506020928301929190910190600101610c84565b5050505092915050565b6000601f82018313610cc857600080fd5b8151610cd6610c578261173b565b91508181835260208401935060208101905083856060840282011115610cfb57600080fd5b60005b83811015610cad5781610d118882610de4565b84525060209092019160609190910190600101610cfe565b6000601f82018313610d3a57600080fd5b8135610d48610c578261173b565b81815260209384019390925082018360005b83811015610cad5781358601610d708882610e3f565b8452506020928301929190910190600101610d5a565b6000610b6882516117f3565b6000610b6882516117c5565b6000601f82018313610daf57600080fd5b8135610dbd610c578261175c565b91508082526020830160208301858383011115610dd957600080fd5b61091c8382846117f8565b600060608284031215610df657600080fd5b610e006060611714565b90506000610e0e8484610f94565b8252506020610e1f84848301610d92565b6020830152506040610e3384828501610d92565b60408301525092915050565b60006101808284031215610e5257600080fd5b610e5d610180611714565b90506000610e6b8484610c20565b8252506020610e7c84848301610c20565b6020830152506040610e9084828501610c20565b6040830152506060610ea484828501610c20565b6060830152506080610eb884828501610f88565b60808301525060a0610ecc84828501610f88565b60a08301525060c0610ee084828501610f88565b60c08301525060e0610ef484828501610f88565b60e083015250610100610f0984828501610f88565b61010083015250610120610f1f84828501610f88565b6101208301525061014082013567ffffffffffffffff811115610f4157600080fd5b610f4d84828501610d9e565b6101408301525061016082013567ffffffffffffffff811115610f6f57600080fd5b610f7b84828501610d9e565b6101608301525092915050565b6000610b6882356117c5565b6000610b6882516117ed565b600060208284031215610fb257600080fd5b6000610fbe8484610c2c565b949350505050565b60008060408385031215610fd957600080fd5b6000610fe58585610c20565b925050602083013567ffffffffffffffff81111561100257600080fd5b61100e85828601610d9e565b9150509250929050565b6000806040838503121561102b57600080fd5b60006110378585610c20565b925050602061100e85828601610f88565b60006020828403121561105a57600080fd5b815167ffffffffffffffff81111561107157600080fd5b610fbe84828501610cb7565b6000806040838503121561109057600080fd5b823567ffffffffffffffff8111156110a757600080fd5b6110b385828601610d29565b925050602083013567ffffffffffffffff8111156110d057600080fd5b61100e85828601610c38565b6000602082840312156110ee57600080fd5b6000610fbe8484610d86565b60006060828403121561110c57600080fd5b6000610fbe8484610de4565b6000806040838503121561112b57600080fd5b823567ffffffffffffffff81111561114257600080fd5b61114e85828601610e3f565b925050602061100e85828601610c20565b60006020828403121561117157600080fd5b6000610fbe8484610d92565b611186816117ac565b82525050565b6000611197826117a8565b8084526020840193506111a9836117a2565b60005b828110156111d9576111bf868351611411565b6111c8826117a2565b6060969096019591506001016111ac565b5093949350505050565b60006111ee826117a8565b80845260208401935083602082028501611207856117a2565b60005b8481101561123e57838303885261122283835161144e565b925061122d826117a2565b60209890980197915060010161120a565b50909695505050505050565b6000611255826117a8565b808452602084019350611267836117a2565b60005b828110156111d95761127d868351611551565b611286826117a2565b6101009690960195915060010161126a565b611186816117c5565b611186816117c8565b60006112b5826117a8565b8084526112c9816020860160208601611804565b6112d281611830565b9093016020019392505050565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601781527f554e535550504f525445445f41535345545f50524f5859000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b602581527f475245415445525f4f525f455155414c5f544f5f345f4c454e4754485f52455160208201527f5549524544000000000000000000000000000000000000000000000000000000604082015260600190565b8051606083019061142284826115e8565b5060208201516114356020850182611298565b5060408201516114486040850182611298565b50505050565b8051600090610180840190611463858261117d565b506020830151611476602086018261117d565b506040830151611489604086018261117d565b50606083015161149c606086018261117d565b5060808301516114af6080860182611298565b5060a08301516114c260a0860182611298565b5060c08301516114d560c0860182611298565b5060e08301516114e860e0860182611298565b506101008301516114fd610100860182611298565b50610120830151611512610120860182611298565b5061014083015184820361014086015261152c82826112aa565b91505061016083015184820361016086015261154882826112aa565b95945050505050565b80516101008301906115638482611298565b5060208201516115766020850182611298565b5060408201516115896040850182611298565b50606082015161159c6060850182611298565b5060808201516115af6080850182611298565b5060a08201516115c260a0850182611298565b5060c08201516115d560c0850182611298565b5060e082015161144860e0850182611298565b611186816117ed565b6020810161096a828461117d565b6040810161160d828561117d565b610b68602083018461117d565b6040808252810161162b818561118c565b90508181036020830152610fbe818461124a565b60208082528101610b6881846111e3565b60208082528101610b68818461124a565b6020810161096a82846112a1565b6020808252810161096a816112df565b6020808252810161096a81611335565b6020808252810161096a81611365565b6020808252810161096a816113bb565b61016081016116be8285611411565b610b686060830184611551565b60208082528101610b68818461144e565b610100810161096a8284611551565b6020810161096a8284611298565b604081016117078285611298565b610b686020830184611298565b60405181810167ffffffffffffffff8111828210171561173357600080fd5b604052919050565b600067ffffffffffffffff82111561175257600080fd5b5060209081020190565b600067ffffffffffffffff82111561177357600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b60200190565b5190565b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b60ff1690565b151590565b82818337506000910152565b60005b8381101561181f578181015183820152602001611807565b838111156114485750506000910152565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016905600a265627a7a723058200742593843de04ccb442f25f2474ff7a322e1281cdb1232118c18b3985166e7f6c6578706572696d656e74616cf50037", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x77 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x4AD1E53 DUP2 EQ PUSH2 0x7C JUMPI DUP1 PUSH4 0x2CD0FC73 EQ PUSH2 0xB3 JUMPI DUP1 PUSH4 0x4B95DE13 EQ PUSH2 0xE1 JUMPI DUP1 PUSH4 0x690D3114 EQ PUSH2 0x10F JUMPI DUP1 PUSH4 0xB6988463 EQ PUSH2 0x13C JUMPI DUP1 PUSH4 0xF241FFB0 EQ PUSH2 0x169 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x88 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x9C PUSH2 0x97 CALLDATASIZE PUSH1 0x4 PUSH2 0x1118 JUMP JUMPDEST PUSH2 0x196 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xAA SWAP3 SWAP2 SWAP1 PUSH2 0x16AF JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xBF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xD3 PUSH2 0xCE CALLDATASIZE PUSH1 0x4 PUSH2 0xFC6 JUMP JUMPDEST PUSH2 0x263 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xAA SWAP3 SWAP2 SWAP1 PUSH2 0x16F9 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xED JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x101 PUSH2 0xFC CALLDATASIZE PUSH1 0x4 PUSH2 0x107D JUMP JUMPDEST PUSH2 0x794 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xAA SWAP3 SWAP2 SWAP1 PUSH2 0x161A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x11B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x12F PUSH2 0x12A CALLDATASIZE PUSH1 0x4 PUSH2 0x107D JUMP JUMPDEST PUSH2 0x86B JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xAA SWAP2 SWAP1 PUSH2 0x1650 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x148 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x15C PUSH2 0x157 CALLDATASIZE PUSH1 0x4 PUSH2 0x1018 JUMP JUMPDEST PUSH2 0x925 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xAA SWAP2 SWAP1 PUSH2 0x15F1 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x175 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x189 PUSH2 0x184 CALLDATASIZE PUSH1 0x4 PUSH2 0x1118 JUMP JUMPDEST PUSH2 0x970 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xAA SWAP2 SWAP1 PUSH2 0x16DC JUMP JUMPDEST PUSH2 0x19E PUSH2 0xBBA JUMP JUMPDEST PUSH2 0x1A6 PUSH2 0xBDA JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x40 MLOAD PUSH32 0xC75E0A8100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP2 AND SWAP1 PUSH4 0xC75E0A81 SWAP1 PUSH2 0x1FC SWAP1 DUP8 SWAP1 PUSH1 0x4 ADD PUSH2 0x16CB JUMP JUMPDEST PUSH1 0x60 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x216 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x22A JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x24E SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x10FA JUMP JUMPDEST SWAP2 POP PUSH2 0x25A DUP5 DUP5 PUSH2 0x970 JUMP JUMPDEST SWAP1 POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP1 DUP1 DUP1 DUP1 DUP1 DUP1 PUSH2 0x27C DUP10 DUP3 PUSH4 0xFFFFFFFF PUSH2 0xA8E AND JUMP JUMPDEST SWAP6 POP PUSH2 0x28F DUP10 PUSH1 0x10 PUSH4 0xFFFFFFFF PUSH2 0xAFB AND JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x40 MLOAD PUSH32 0x6070410800000000000000000000000000000000000000000000000000000000 DUP2 MSTORE SWAP2 SWAP7 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 PUSH4 0x60704108 SWAP1 PUSH2 0x2E6 SWAP1 DUP10 SWAP1 PUSH1 0x4 ADD PUSH2 0x1661 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x300 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x314 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x338 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0xFA0 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x4552433230546F6B656E28616464726573732900000000000000000000000000 DUP2 MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x13 ADD SWAP1 KECCAK256 SWAP1 SWAP5 POP PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP8 DUP2 AND SWAP2 AND EQ ISZERO PUSH2 0x4ED JUMPI PUSH1 0x40 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND SWAP1 PUSH4 0x70A08231 SWAP1 PUSH2 0x3EB SWAP1 DUP14 SWAP1 PUSH1 0x4 ADD PUSH2 0x15F1 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x405 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x419 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x43D SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x115F JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0xDD62ED3E00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE SWAP1 SWAP9 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND SWAP1 PUSH4 0xDD62ED3E SWAP1 PUSH2 0x494 SWAP1 DUP14 SWAP1 DUP9 SWAP1 PUSH1 0x4 ADD PUSH2 0x15FF JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x4AE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x4C2 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x4E6 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x115F JUMP JUMPDEST SWAP7 POP PUSH2 0x787 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x455243373231546F6B656E28616464726573732C75696E743235362900000000 DUP2 MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x1C ADD SWAP1 KECCAK256 PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP8 DUP2 AND SWAP2 AND EQ ISZERO PUSH2 0x74C JUMPI PUSH2 0x55C DUP10 PUSH1 0x24 PUSH4 0xFFFFFFFF PUSH2 0xB5C AND JUMP JUMPDEST SWAP3 POP PUSH2 0x568 DUP6 DUP5 PUSH2 0x925 JUMP JUMPDEST SWAP2 POP DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP11 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ PUSH2 0x5A4 JUMPI PUSH1 0x0 PUSH2 0x5A7 JUMP JUMPDEST PUSH1 0x1 JUMPDEST PUSH1 0xFF AND SWAP8 POP DUP5 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0xE985E9C5 DUP12 DUP7 PUSH1 0x40 MLOAD DUP4 PUSH4 0xFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x603 SWAP3 SWAP2 SWAP1 PUSH2 0x15FF JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x61D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x631 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x655 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x10DC JUMP JUMPDEST DUP1 PUSH2 0x731 JUMPI POP DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0x81812FC DUP6 PUSH1 0x40 MLOAD DUP3 PUSH4 0xFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x6C7 SWAP2 SWAP1 PUSH2 0x16EB JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x6E1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x6F5 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x719 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0xFA0 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ JUMPDEST SWAP1 POP DUP1 PUSH2 0x73F JUMPI PUSH1 0x0 PUSH2 0x742 JUMP JUMPDEST PUSH1 0x1 JUMPDEST PUSH1 0xFF AND SWAP7 POP PUSH2 0x787 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x77E SWAP1 PUSH2 0x167F JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST POP POP POP POP POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x40 MLOAD PUSH32 0x7E9D74DC00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x60 SWAP2 DUP3 SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP2 AND SWAP1 PUSH4 0x7E9D74DC SWAP1 PUSH2 0x7EF SWAP1 DUP8 SWAP1 PUSH1 0x4 ADD PUSH2 0x163F JUMP JUMPDEST PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x809 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x81D JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x0 DUP3 RETURNDATACOPY PUSH1 0x1F RETURNDATASIZE SWAP1 DUP2 ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND DUP3 ADD PUSH1 0x40 MSTORE PUSH2 0x863 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x1048 JUMP JUMPDEST SWAP2 POP PUSH2 0x25A DUP5 DUP5 JUMPDEST PUSH1 0x60 PUSH1 0x0 PUSH1 0x60 PUSH1 0x0 DUP6 MLOAD SWAP3 POP DUP3 PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0x8B1 JUMPI DUP2 PUSH1 0x20 ADD JUMPDEST PUSH2 0x89E PUSH2 0xBDA JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD SWAP1 PUSH1 0x1 SWAP1 SUB SWAP1 DUP2 PUSH2 0x896 JUMPI SWAP1 POP JUMPDEST POP SWAP2 POP PUSH1 0x0 SWAP1 POP JUMPDEST DUP1 DUP4 EQ PUSH2 0x918 JUMPI PUSH2 0x8F8 DUP7 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x8D1 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP7 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x8E9 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x970 JUMP JUMPDEST DUP3 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x906 JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MSTORE PUSH1 0x1 ADD PUSH2 0x8B9 JUMP JUMPDEST DUP2 SWAP4 POP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x40 MLOAD PUSH32 0x6352211E00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE DUP3 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x20 DUP2 PUSH1 0x24 DUP4 DUP8 GAS STATICCALL DUP1 ISZERO PUSH2 0x967 JUMPI DUP2 MLOAD SWAP3 POP JUMPDEST POP POP JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH2 0x978 PUSH2 0xBDA JUMP JUMPDEST PUSH1 0x60 PUSH2 0x98D DUP5 PUSH1 0x0 ADD MLOAD DUP6 PUSH2 0x140 ADD MLOAD PUSH2 0x263 JUMP JUMPDEST PUSH1 0x20 DUP5 ADD MSTORE DUP3 MSTORE PUSH2 0x160 DUP5 ADD MLOAD PUSH2 0x9A5 SWAP1 DUP5 SWAP1 PUSH2 0x263 JUMP JUMPDEST PUSH1 0x60 DUP5 ADD MSTORE PUSH1 0x40 DUP1 DUP5 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x1 DUP1 SLOAD DUP3 MLOAD PUSH1 0x20 PUSH1 0x1F PUSH1 0x2 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH2 0x100 DUP7 DUP9 AND ISZERO MUL ADD SWAP1 SWAP5 AND SWAP4 SWAP1 SWAP4 DIV SWAP3 DUP4 ADD DUP2 SWAP1 DIV DUP2 MUL DUP3 ADD DUP2 ADD SWAP1 SWAP5 MSTORE DUP2 DUP2 MSTORE SWAP3 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0xA54 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0xA29 JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0xA54 JUMP JUMPDEST DUP3 ADD SWAP2 SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD DUP2 MSTORE SWAP1 PUSH1 0x1 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0xA37 JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP POP POP SWAP1 POP PUSH2 0xA69 DUP5 PUSH1 0x0 ADD MLOAD DUP3 PUSH2 0x263 JUMP JUMPDEST PUSH1 0xA0 DUP5 ADD MSTORE PUSH1 0x80 DUP4 ADD MSTORE PUSH2 0xA7D DUP4 DUP3 PUSH2 0x263 JUMP JUMPDEST PUSH1 0xE0 DUP5 ADD MSTORE PUSH1 0xC0 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x4 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0xAD0 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x77E SWAP1 PUSH2 0x169F JUMP JUMPDEST POP POP PUSH1 0x20 ADD MLOAD PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x14 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0xB3D JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x77E SWAP1 PUSH2 0x168F JUMP JUMPDEST POP ADD PUSH1 0x14 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xB68 DUP4 DUP4 PUSH2 0xB6F JUMP JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x20 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0xBB1 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x77E SWAP1 PUSH2 0x166F JUMP JUMPDEST POP ADD PUSH1 0x20 ADD MLOAD SWAP1 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x60 DUP2 ADD DUP3 MSTORE PUSH1 0x0 DUP1 DUP3 MSTORE PUSH1 0x20 DUP3 ADD DUP2 SWAP1 MSTORE SWAP2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE SWAP1 JUMP JUMPDEST PUSH2 0x100 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xB68 DUP3 CALLDATALOAD PUSH2 0x17AC JUMP JUMPDEST PUSH1 0x0 PUSH2 0xB68 DUP3 MLOAD PUSH2 0x17AC JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xC49 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xC5C PUSH2 0xC57 DUP3 PUSH2 0x173B JUMP JUMPDEST PUSH2 0x1714 JUMP JUMPDEST SWAP2 POP DUP2 DUP2 DUP4 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH1 0x20 DUP2 ADD SWAP1 POP DUP4 DUP6 PUSH1 0x20 DUP5 MUL DUP3 ADD GT ISZERO PUSH2 0xC81 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xCAD JUMPI DUP2 PUSH2 0xC97 DUP9 DUP3 PUSH2 0xC20 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0xC84 JUMP JUMPDEST POP POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xCC8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 MLOAD PUSH2 0xCD6 PUSH2 0xC57 DUP3 PUSH2 0x173B JUMP JUMPDEST SWAP2 POP DUP2 DUP2 DUP4 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH1 0x20 DUP2 ADD SWAP1 POP DUP4 DUP6 PUSH1 0x60 DUP5 MUL DUP3 ADD GT ISZERO PUSH2 0xCFB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xCAD JUMPI DUP2 PUSH2 0xD11 DUP9 DUP3 PUSH2 0xDE4 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x60 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0xCFE JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xD3A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xD48 PUSH2 0xC57 DUP3 PUSH2 0x173B JUMP JUMPDEST DUP2 DUP2 MSTORE PUSH1 0x20 SWAP4 DUP5 ADD SWAP4 SWAP1 SWAP3 POP DUP3 ADD DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xCAD JUMPI DUP2 CALLDATALOAD DUP7 ADD PUSH2 0xD70 DUP9 DUP3 PUSH2 0xE3F JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0xD5A JUMP JUMPDEST PUSH1 0x0 PUSH2 0xB68 DUP3 MLOAD PUSH2 0x17F3 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xB68 DUP3 MLOAD PUSH2 0x17C5 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xDAF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xDBD PUSH2 0xC57 DUP3 PUSH2 0x175C JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH2 0xDD9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x91C DUP4 DUP3 DUP5 PUSH2 0x17F8 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x60 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xDF6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xE00 PUSH1 0x60 PUSH2 0x1714 JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0xE0E DUP5 DUP5 PUSH2 0xF94 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0xE1F DUP5 DUP5 DUP4 ADD PUSH2 0xD92 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0xE33 DUP5 DUP3 DUP6 ADD PUSH2 0xD92 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x180 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xE52 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xE5D PUSH2 0x180 PUSH2 0x1714 JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0xE6B DUP5 DUP5 PUSH2 0xC20 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0xE7C DUP5 DUP5 DUP4 ADD PUSH2 0xC20 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0xE90 DUP5 DUP3 DUP6 ADD PUSH2 0xC20 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP PUSH1 0x60 PUSH2 0xEA4 DUP5 DUP3 DUP6 ADD PUSH2 0xC20 JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MSTORE POP PUSH1 0x80 PUSH2 0xEB8 DUP5 DUP3 DUP6 ADD PUSH2 0xF88 JUMP JUMPDEST PUSH1 0x80 DUP4 ADD MSTORE POP PUSH1 0xA0 PUSH2 0xECC DUP5 DUP3 DUP6 ADD PUSH2 0xF88 JUMP JUMPDEST PUSH1 0xA0 DUP4 ADD MSTORE POP PUSH1 0xC0 PUSH2 0xEE0 DUP5 DUP3 DUP6 ADD PUSH2 0xF88 JUMP JUMPDEST PUSH1 0xC0 DUP4 ADD MSTORE POP PUSH1 0xE0 PUSH2 0xEF4 DUP5 DUP3 DUP6 ADD PUSH2 0xF88 JUMP JUMPDEST PUSH1 0xE0 DUP4 ADD MSTORE POP PUSH2 0x100 PUSH2 0xF09 DUP5 DUP3 DUP6 ADD PUSH2 0xF88 JUMP JUMPDEST PUSH2 0x100 DUP4 ADD MSTORE POP PUSH2 0x120 PUSH2 0xF1F DUP5 DUP3 DUP6 ADD PUSH2 0xF88 JUMP JUMPDEST PUSH2 0x120 DUP4 ADD MSTORE POP PUSH2 0x140 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xF41 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xF4D DUP5 DUP3 DUP6 ADD PUSH2 0xD9E JUMP JUMPDEST PUSH2 0x140 DUP4 ADD MSTORE POP PUSH2 0x160 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xF6F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xF7B DUP5 DUP3 DUP6 ADD PUSH2 0xD9E JUMP JUMPDEST PUSH2 0x160 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0xB68 DUP3 CALLDATALOAD PUSH2 0x17C5 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xB68 DUP3 MLOAD PUSH2 0x17ED JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xFB2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xFBE DUP5 DUP5 PUSH2 0xC2C JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0xFD9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xFE5 DUP6 DUP6 PUSH2 0xC20 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 DUP4 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x1002 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x100E DUP6 DUP3 DUP7 ADD PUSH2 0xD9E JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x102B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x1037 DUP6 DUP6 PUSH2 0xC20 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x100E DUP6 DUP3 DUP7 ADD PUSH2 0xF88 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x105A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 MLOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x1071 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xFBE DUP5 DUP3 DUP6 ADD PUSH2 0xCB7 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x1090 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP3 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x10A7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x10B3 DUP6 DUP3 DUP7 ADD PUSH2 0xD29 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 DUP4 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x10D0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x100E DUP6 DUP3 DUP7 ADD PUSH2 0xC38 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x10EE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xFBE DUP5 DUP5 PUSH2 0xD86 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x60 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x110C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xFBE DUP5 DUP5 PUSH2 0xDE4 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x112B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP3 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x1142 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x114E DUP6 DUP3 DUP7 ADD PUSH2 0xE3F JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x100E DUP6 DUP3 DUP7 ADD PUSH2 0xC20 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x1171 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xFBE DUP5 DUP5 PUSH2 0xD92 JUMP JUMPDEST PUSH2 0x1186 DUP2 PUSH2 0x17AC JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x1197 DUP3 PUSH2 0x17A8 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH2 0x11A9 DUP4 PUSH2 0x17A2 JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x11D9 JUMPI PUSH2 0x11BF DUP7 DUP4 MLOAD PUSH2 0x1411 JUMP JUMPDEST PUSH2 0x11C8 DUP3 PUSH2 0x17A2 JUMP JUMPDEST PUSH1 0x60 SWAP7 SWAP1 SWAP7 ADD SWAP6 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x11AC JUMP JUMPDEST POP SWAP4 SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x11EE DUP3 PUSH2 0x17A8 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP DUP4 PUSH1 0x20 DUP3 MUL DUP6 ADD PUSH2 0x1207 DUP6 PUSH2 0x17A2 JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP5 DUP2 LT ISZERO PUSH2 0x123E JUMPI DUP4 DUP4 SUB DUP9 MSTORE PUSH2 0x1222 DUP4 DUP4 MLOAD PUSH2 0x144E JUMP JUMPDEST SWAP3 POP PUSH2 0x122D DUP3 PUSH2 0x17A2 JUMP JUMPDEST PUSH1 0x20 SWAP9 SWAP1 SWAP9 ADD SWAP8 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x120A JUMP JUMPDEST POP SWAP1 SWAP7 SWAP6 POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x1255 DUP3 PUSH2 0x17A8 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH2 0x1267 DUP4 PUSH2 0x17A2 JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x11D9 JUMPI PUSH2 0x127D DUP7 DUP4 MLOAD PUSH2 0x1551 JUMP JUMPDEST PUSH2 0x1286 DUP3 PUSH2 0x17A2 JUMP JUMPDEST PUSH2 0x100 SWAP7 SWAP1 SWAP7 ADD SWAP6 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x126A JUMP JUMPDEST PUSH2 0x1186 DUP2 PUSH2 0x17C5 JUMP JUMPDEST PUSH2 0x1186 DUP2 PUSH2 0x17C8 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x12B5 DUP3 PUSH2 0x17A8 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH2 0x12C9 DUP2 PUSH1 0x20 DUP7 ADD PUSH1 0x20 DUP7 ADD PUSH2 0x1804 JUMP JUMPDEST PUSH2 0x12D2 DUP2 PUSH2 0x1830 JUMP JUMPDEST SWAP1 SWAP4 ADD PUSH1 0x20 ADD SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F33325F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x17 DUP2 MSTORE PUSH32 0x554E535550504F525445445F41535345545F50524F5859000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F32305F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x25 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F345F4C454E4754485F524551 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5549524544000000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST DUP1 MLOAD PUSH1 0x60 DUP4 ADD SWAP1 PUSH2 0x1422 DUP5 DUP3 PUSH2 0x15E8 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x1435 PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x1448 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP POP POP POP JUMP JUMPDEST DUP1 MLOAD PUSH1 0x0 SWAP1 PUSH2 0x180 DUP5 ADD SWAP1 PUSH2 0x1463 DUP6 DUP3 PUSH2 0x117D JUMP JUMPDEST POP PUSH1 0x20 DUP4 ADD MLOAD PUSH2 0x1476 PUSH1 0x20 DUP7 ADD DUP3 PUSH2 0x117D JUMP JUMPDEST POP PUSH1 0x40 DUP4 ADD MLOAD PUSH2 0x1489 PUSH1 0x40 DUP7 ADD DUP3 PUSH2 0x117D JUMP JUMPDEST POP PUSH1 0x60 DUP4 ADD MLOAD PUSH2 0x149C PUSH1 0x60 DUP7 ADD DUP3 PUSH2 0x117D JUMP JUMPDEST POP PUSH1 0x80 DUP4 ADD MLOAD PUSH2 0x14AF PUSH1 0x80 DUP7 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0xA0 DUP4 ADD MLOAD PUSH2 0x14C2 PUSH1 0xA0 DUP7 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0xC0 DUP4 ADD MLOAD PUSH2 0x14D5 PUSH1 0xC0 DUP7 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0xE0 DUP4 ADD MLOAD PUSH2 0x14E8 PUSH1 0xE0 DUP7 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH2 0x100 DUP4 ADD MLOAD PUSH2 0x14FD PUSH2 0x100 DUP7 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH2 0x120 DUP4 ADD MLOAD PUSH2 0x1512 PUSH2 0x120 DUP7 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH2 0x140 DUP4 ADD MLOAD DUP5 DUP3 SUB PUSH2 0x140 DUP7 ADD MSTORE PUSH2 0x152C DUP3 DUP3 PUSH2 0x12AA JUMP JUMPDEST SWAP2 POP POP PUSH2 0x160 DUP4 ADD MLOAD DUP5 DUP3 SUB PUSH2 0x160 DUP7 ADD MSTORE PUSH2 0x1548 DUP3 DUP3 PUSH2 0x12AA JUMP JUMPDEST SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST DUP1 MLOAD PUSH2 0x100 DUP4 ADD SWAP1 PUSH2 0x1563 DUP5 DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x1576 PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x1589 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0x60 DUP3 ADD MLOAD PUSH2 0x159C PUSH1 0x60 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0x80 DUP3 ADD MLOAD PUSH2 0x15AF PUSH1 0x80 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0xA0 DUP3 ADD MLOAD PUSH2 0x15C2 PUSH1 0xA0 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0xC0 DUP3 ADD MLOAD PUSH2 0x15D5 PUSH1 0xC0 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST POP PUSH1 0xE0 DUP3 ADD MLOAD PUSH2 0x1448 PUSH1 0xE0 DUP6 ADD DUP3 PUSH2 0x1298 JUMP JUMPDEST PUSH2 0x1186 DUP2 PUSH2 0x17ED JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x96A DUP3 DUP5 PUSH2 0x117D JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x160D DUP3 DUP6 PUSH2 0x117D JUMP JUMPDEST PUSH2 0xB68 PUSH1 0x20 DUP4 ADD DUP5 PUSH2 0x117D JUMP JUMPDEST PUSH1 0x40 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x162B DUP2 DUP6 PUSH2 0x118C JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0xFBE DUP2 DUP5 PUSH2 0x124A JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xB68 DUP2 DUP5 PUSH2 0x11E3 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xB68 DUP2 DUP5 PUSH2 0x124A JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x96A DUP3 DUP5 PUSH2 0x12A1 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x96A DUP2 PUSH2 0x12DF JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x96A DUP2 PUSH2 0x1335 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x96A DUP2 PUSH2 0x1365 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x96A DUP2 PUSH2 0x13BB JUMP JUMPDEST PUSH2 0x160 DUP2 ADD PUSH2 0x16BE DUP3 DUP6 PUSH2 0x1411 JUMP JUMPDEST PUSH2 0xB68 PUSH1 0x60 DUP4 ADD DUP5 PUSH2 0x1551 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xB68 DUP2 DUP5 PUSH2 0x144E JUMP JUMPDEST PUSH2 0x100 DUP2 ADD PUSH2 0x96A DUP3 DUP5 PUSH2 0x1551 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x96A DUP3 DUP5 PUSH2 0x1298 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x1707 DUP3 DUP6 PUSH2 0x1298 JUMP JUMPDEST PUSH2 0xB68 PUSH1 0x20 DUP4 ADD DUP5 PUSH2 0x1298 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0x1733 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x1752 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 SWAP1 DUP2 MUL ADD SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x1773 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND ADD SWAP1 JUMP JUMPDEST PUSH1 0x20 ADD SWAP1 JUMP JUMPDEST MLOAD SWAP1 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND SWAP1 JUMP JUMPDEST PUSH1 0xFF AND SWAP1 JUMP JUMPDEST ISZERO ISZERO SWAP1 JUMP JUMPDEST DUP3 DUP2 DUP4 CALLDATACOPY POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x181F JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x1807 JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0x1448 JUMPI POP POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x1F ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND SWAP1 JUMP STOP LOG2 PUSH6 0x627A7A723058 KECCAK256 SMOD TIMESTAMP MSIZE CODESIZE NUMBER 0xde DIV 0xcc 0xb4 TIMESTAMP CALLCODE 0x5f 0x24 PUSH21 0xFF7A322E1281CDB1232118C18B3985166E7F6C6578 PUSH17 0x6572696D656E74616CF500370000000000 ", - "sourceMap": "898:7576:11:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2336:352;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;2336:352:11;;;;;;;;;;;;;;;;;;;;;;;;;;5614:1359;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;5614:1359:11;;;;;;;;;;;;;;;;;;3018:384;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3018:384:11;;;;;;;;;;;;;;;;;;4716:452;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4716:452:11;;;;;;;;;;;;;;;;;7258:1214;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;7258:1214:11;;;;;;;;;;;;;;;;;3661:739;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3661:739:11;;;;;;;;;;;;;;;;;2336:352;2463:35;;:::i;:::-;2500:28;;:::i;:::-;2556:8;;:28;;;;;:8;;;;;:21;;:28;;2578:5;;2556:28;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2556:28:11;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;2556:28:11;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;2556:28:11;;;;;;;;;2544:40;;2607:34;2621:5;2628:12;2607:13;:34::i;:::-;2594:47;-1:-1:-1;2336:352:11;;;;;:::o;5614:1359::-;5731:15;;;;;;;;5803:23;:9;5731:15;5803:23;:20;:23;:::i;:::-;5781:45;-1:-1:-1;5852:25:11;:9;5874:2;5852:25;:21;:25;:::i;:::-;5908:8;;:36;;;;;5836:41;;-1:-1:-1;5908:8:11;;;:22;;:36;;5931:12;;5908:36;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;5908:36:11;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;5908:36:11;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;5908:36:11;;;;;;;;;977:32;;;;;;;;;;;;;;;;5887:57;;-1:-1:-1;5959:29:11;;;;;;;5955:975;;;6043:36;;;;;:28;;;;;;:36;;6072:6;;6043:36;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;6043:36:11;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;6043:36:11;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;6043:36:11;;;;;;;;;6137:48;;;;;6033:46;;-1:-1:-1;6137:28:11;;;;;;:48;;6166:6;;6174:10;;6137:48;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;6137:48:11;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;6137:48:11;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;6137:48:11;;;;;;;;;6125:60;;5955:975;;;1065:41;;;;;;;;;;;;;;;;6206:30;;;;;;;6202:728;;;6270:25;:9;6292:2;6270:25;:21;:25;:::i;:::-;6252:43;;6364:35;6384:5;6391:7;6364:19;:35::i;:::-;6348:51;;6496:5;6486:15;;:6;:15;;;:23;;6508:1;6486:23;;;6504:1;6486:23;6476:33;;;;6620:5;6607:36;;;6644:6;6652:10;6607:56;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;6607:56:11;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;6607:56:11;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;6607:56:11;;;;;;;;;:114;;;;6711:10;6667:54;;6680:5;6667:31;;;6699:7;6667:40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;6667:40:11;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;6667:40:11;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;6667:40:11;;;;;;;;;:54;;;6607:114;6589:132;;6837:10;:18;;6854:1;6837:18;;;6850:1;6837:18;6825:30;;;;6202:728;;;6886:33;;;;;;;;;;;;;;;;;;;6202:728;5614:1359;;;;;;;;;;;:::o;3018:384::-;3261:8;;:30;;;;;3161:38;;;;3261:8;;;;;:22;;:30;;3284:6;;3261:30;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3261:30:11;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3261:30:11;;;;;;39:16:-1;36:1;17:17;2:54;101:4;3261:30:11;80:15:-1;;;97:9;76:31;65:43;;120:4;113:20;3261:30:11;;;;;;;;;3248:43;;3315:38;3330:6;3338:14;4716:452;4850:12;4885:20;4931:31;5010:9;4908:6;:13;4885:36;;4982:12;4965:30;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;4931:64;;5022:1;5010:13;;5005:129;5025:17;;;5005:129;;5080:43;5094:6;5101:1;5094:9;;;;;;;;;;;;;;;;;;5105:14;5120:1;5105:17;;;;;;;;;;;;;;;;;;5080:13;:43::i;:::-;5063:11;5075:1;5063:14;;;;;;;;;;;;;;;;;;:60;5044:3;;5005:129;;;5150:11;5143:18;;4716:452;;;;;;;;:::o;7258:1214::-;7364:13;7477:2;7471:9;7574:66;7565:7;7558:83;7678:7;7674:1;7665:7;7661:15;7654:32;8100:2;8047:7;7990:2;7943:7;7894:5;7849:3;7821:332;8280:7;8277:2;;;8321:7;8315:14;8306:23;;8277:2;-1:-1:-1;;7258:1214:11;;;;;:::o;3661:739::-;3780:28;;:::i;:::-;4076:25;3879:64;3902:5;:18;;;3922:5;:20;;;3879:22;:64::i;:::-;3850:25;;;3824:119;;;4045:20;;;;4008:58;;4031:12;;4008:22;:58::i;:::-;3979:25;;;3953:113;3954:23;;;;3953:113;;;;4104:14;4076:42;;;;-1:-1:-1;4076:42:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4104:14;4076:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4189:56;4212:5;:18;;;4232:12;4189:22;:56::i;:::-;4157:28;;;4128:117;4129:26;;;4128:117;4316:50;4339:12;4353;4316:22;:50::i;:::-;4284:28;;;4255:111;4256:26;;;4255:111;3661:739;;;;;:::o;15559:559:48:-;15679:13;15741:5;15749:1;15741:9;15729:1;:8;:21;;15708:105;;;;;;;;;;;;;;;;-1:-1:-1;;15869:2:48;15862:10;15856:17;16012:66;16000:79;;15559:559::o;10259:886::-;10380:14;10443:5;10451:2;10443:10;10431:1;:8;:22;;10410:135;;;;;;;;;;;;;;;;-1:-1:-1;11047:13:48;10792:2;11047:13;11041:20;11063:42;11037:69;;10259:886::o;14699:195::-;14820:14;14865:21;14877:1;14880:5;14865:11;:21::i;:::-;14857:30;14699:195;-1:-1:-1;;;14699:195:48:o;13281:490::-;13402:14;13465:5;13473:2;13465:10;13453:1;:8;:22;;13432:107;;;;;;;;;;;;;;;;-1:-1:-1;13718:13:48;13620:2;13718:13;13712:20;;13281:490::o;898:7576:11:-;;;;;;;;;-1:-1:-1;898:7576:11;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;5:118:-1:-;;72:46;110:6;97:20;72:46;;130:122;;208:39;239:6;233:13;208:39;;277:707;;387:4;375:17;;371:27;-1:-1;361:2;;412:1;409;402:12;361:2;449:6;436:20;471:80;486:64;543:6;486:64;;;471:80;;;462:89;;568:5;593:6;586:5;579:21;623:4;615:6;611:17;601:27;;645:4;640:3;636:14;629:21;;698:6;745:3;737:4;729:6;725:17;720:3;716:27;713:36;710:2;;;762:1;759;752:12;710:2;787:1;772:206;797:6;794:1;791:13;772:206;;;855:3;877:37;910:3;898:10;877:37;;;865:50;;-1:-1;938:4;929:14;;;;957;;;;;819:1;812:9;772:206;;;776:14;354:630;;;;;;;;1028:791;;1172:4;1160:17;;1156:27;-1:-1;1146:2;;1197:1;1194;1187:12;1146:2;1227:6;1221:13;1249:103;1264:87;1344:6;1264:87;;1249:103;1240:112;;1369:5;1394:6;1387:5;1380:21;1424:4;1416:6;1412:17;1402:27;;1446:4;1441:3;1437:14;1430:21;;1499:6;1546:3;1538:4;1530:6;1526:17;1521:3;1517:27;1514:36;1511:2;;;1563:1;1560;1553:12;1511:2;1588:1;1573:240;1598:6;1595:1;1592:13;1573:240;;;1656:3;1678:71;1745:3;1733:10;1678:71;;;1666:84;;-1:-1;1773:4;1764:14;;;;1801:4;1792:14;;;;;1620:1;1613:9;1573:240;;1859:735;;1988:4;1976:17;;1972:27;-1:-1;1962:2;;2013:1;2010;2003:12;1962:2;2050:6;2037:20;2072:99;2087:83;2163:6;2087:83;;2072:99;2199:21;;;2243:4;2231:17;;;;2063:108;;-1:-1;2256:14;;2231:17;2351:1;2336:252;2361:6;2358:1;2355:13;2336:252;;;2444:3;2431:17;2423:6;2419:30;2468:56;2520:3;2508:10;2468:56;;;2456:69;;-1:-1;2548:4;2539:14;;;;2567;;;;;2383:1;2376:9;2336:252;;2602:116;;2677:36;2705:6;2699:13;2677:36;;2725:122;;2803:39;2834:6;2828:13;2803:39;;2855:432;;2945:4;2933:17;;2929:27;-1:-1;2919:2;;2970:1;2967;2960:12;2919:2;3007:6;2994:20;3029:60;3044:44;3081:6;3044:44;;3029:60;3020:69;;3109:6;3102:5;3095:21;3145:4;3137:6;3133:17;3178:4;3171:5;3167:16;3213:3;3204:6;3199:3;3195:16;3192:25;3189:2;;;3230:1;3227;3220:12;3189:2;3240:41;3274:6;3269:3;3264;3240:41;;3776:685;;3899:4;3887:9;3882:3;3878:19;3874:30;3871:2;;;3917:1;3914;3907:12;3871:2;3935:20;3950:4;3935:20;;;3926:29;-1:-1;4012:1;4043:58;4097:3;4077:9;4043:58;;;4019:83;;-1:-1;4168:2;4201:60;4257:3;4233:22;;;4201:60;;;4194:4;4187:5;4183:16;4176:86;4123:150;4346:2;4379:60;4435:3;4426:6;4415:9;4411:22;4379:60;;;4372:4;4365:5;4361:16;4354:86;4283:168;3865:596;;;;;5224:2205;;5332:5;5320:9;5315:3;5311:19;5307:31;5304:2;;;5351:1;5348;5341:12;5304:2;5369:21;5384:5;5369:21;;;5360:30;-1:-1;5448:1;5479:49;5524:3;5504:9;5479:49;;;5455:74;;-1:-1;5598:2;5631:49;5676:3;5652:22;;;5631:49;;;5624:4;5617:5;5613:16;5606:75;5550:142;5757:2;5790:49;5835:3;5826:6;5815:9;5811:22;5790:49;;;5783:4;5776:5;5772:16;5765:75;5702:149;5910:2;5943:49;5988:3;5979:6;5968:9;5964:22;5943:49;;;5936:4;5929:5;5925:16;5918:75;5861:143;6066:3;6100:49;6145:3;6136:6;6125:9;6121:22;6100:49;;;6093:4;6086:5;6082:16;6075:75;6014:147;6223:3;6257:49;6302:3;6293:6;6282:9;6278:22;6257:49;;;6250:4;6243:5;6239:16;6232:75;6171:147;6372:3;6406:49;6451:3;6442:6;6431:9;6427:22;6406:49;;;6399:4;6392:5;6388:16;6381:75;6328:139;6521:3;6555:49;6600:3;6591:6;6580:9;6576:22;6555:49;;;6548:4;6541:5;6537:16;6530:75;6477:139;6683:3;6718:49;6763:3;6754:6;6743:9;6739:22;6718:49;;;6710:5;6703;6699:17;6692:76;6626:153;6829:3;6864:49;6909:3;6900:6;6889:9;6885:22;6864:49;;;6856:5;6849;6845:17;6838:76;6789:136;7013:3;7002:9;6998:19;6985:33;7038:18;7030:6;7027:30;7024:2;;;7070:1;7067;7060:12;7024:2;7106:54;7156:3;7147:6;7136:9;7132:22;7106:54;;;7098:5;7091;7087:17;7080:81;6935:237;7260:3;7249:9;7245:19;7232:33;7285:18;7277:6;7274:30;7271:2;;;7317:1;7314;7307:12;7271:2;7353:54;7403:3;7394:6;7383:9;7379:22;7353:54;;;7345:5;7338;7334:17;7327:81;7182:237;5298:2131;;;;;9680:118;;9747:46;9785:6;9772:20;9747:46;;9934:118;;10010:37;10039:6;10033:13;10010:37;;10059:263;;10174:2;10162:9;10153:7;10149:23;10145:32;10142:2;;;10190:1;10187;10180:12;10142:2;10225:1;10242:64;10298:7;10278:9;10242:64;;;10232:74;10136:186;-1:-1;;;;10136:186;10329:470;;;10459:2;10447:9;10438:7;10434:23;10430:32;10427:2;;;10475:1;10472;10465:12;10427:2;10510:1;10527:53;10572:7;10552:9;10527:53;;;10517:63;;10489:97;10645:2;10634:9;10630:18;10617:32;10669:18;10661:6;10658:30;10655:2;;;10701:1;10698;10691:12;10655:2;10721:62;10775:7;10766:6;10755:9;10751:22;10721:62;;;10711:72;;10596:193;10421:378;;;;;;10806:366;;;10927:2;10915:9;10906:7;10902:23;10898:32;10895:2;;;10943:1;10940;10933:12;10895:2;10978:1;10995:53;11040:7;11020:9;10995:53;;;10985:63;;10957:97;11085:2;11103:53;11148:7;11139:6;11128:9;11124:22;11103:53;;11179:438;;11342:2;11330:9;11321:7;11317:23;11313:32;11310:2;;;11358:1;11355;11348:12;11310:2;11393:24;;11437:18;11426:30;;11423:2;;;11469:1;11466;11459:12;11423:2;11489:112;11593:7;11584:6;11573:9;11569:22;11489:112;;11624:676;;;11814:2;11802:9;11793:7;11789:23;11785:32;11782:2;;;11830:1;11827;11820:12;11782:2;11865:31;;11916:18;11905:30;;11902:2;;;11948:1;11945;11938:12;11902:2;11968:97;12057:7;12048:6;12037:9;12033:22;11968:97;;;11958:107;;11844:227;12130:2;12119:9;12115:18;12102:32;12154:18;12146:6;12143:30;12140:2;;;12186:1;12183;12176:12;12140:2;12206:78;12276:7;12267:6;12256:9;12252:22;12206:78;;12307:257;;12419:2;12407:9;12398:7;12394:23;12390:32;12387:2;;;12435:1;12432;12425:12;12387:2;12470:1;12487:61;12540:7;12520:9;12487:61;;12571:317;;12713:2;12701:9;12692:7;12688:23;12684:32;12681:2;;;12729:1;12726;12719:12;12681:2;12764:1;12781:91;12864:7;12844:9;12781:91;;12895:498;;;13039:2;13027:9;13018:7;13014:23;13010:32;13007:2;;;13055:1;13052;13045:12;13007:2;13090:31;;13141:18;13130:30;;13127:2;;;13173:1;13170;13163:12;13127:2;13193:76;13261:7;13252:6;13241:9;13237:22;13193:76;;;13183:86;;13069:206;13306:2;13324:53;13369:7;13360:6;13349:9;13345:22;13324:53;;13400:263;;13515:2;13503:9;13494:7;13490:23;13486:32;13483:2;;;13531:1;13528;13521:12;13483:2;13566:1;13583:64;13639:7;13619:9;13583:64;;13670:110;13743:31;13768:5;13743:31;;;13738:3;13731:44;13725:55;;;13854:755;;14035:77;14106:5;14035:77;;;14130:6;14125:3;14118:19;14154:4;14149:3;14145:14;14138:21;;14199:79;14272:5;14199:79;;;14299:1;14284:303;14309:6;14306:1;14303:13;14284:303;;;14349:103;14448:3;14439:6;14433:13;14349:103;;;14469:83;14545:6;14469:83;;;14575:4;14566:14;;;;;14459:93;-1:-1;14331:1;14324:9;14284:303;;;-1:-1;14600:3;;14014:595;-1:-1;;;;14014:595;14676:864;;14849:73;14916:5;14849:73;;;14940:6;14935:3;14928:19;14964:4;14959:3;14955:14;14948:21;;15012:3;15054:4;15046:6;15042:17;15037:3;15033:27;15080:75;15149:5;15080:75;;;15176:1;15161:340;15186:6;15183:1;15180:13;15161:340;;;15248:9;15242:4;15238:20;15233:3;15226:33;15274:96;15365:4;15356:6;15350:13;15274:96;;;15266:104;;15387:79;15459:6;15387:79;;;15489:4;15480:14;;;;;15377:89;-1:-1;15208:1;15201:9;15161:340;;;-1:-1;15514:4;;14828:712;-1:-1;;;;;;14828:712;15629:749;;15808:76;15878:5;15808:76;;;15902:6;15897:3;15890:19;15926:4;15921:3;15917:14;15910:21;;15971:78;16043:5;15971:78;;;16070:1;16055:301;16080:6;16077:1;16074:13;16055:301;;;16120:101;16217:3;16208:6;16202:13;16120:101;;;16238:82;16313:6;16238:82;;;16343:5;16334:15;;;;;16228:92;-1:-1;16102:1;16095:9;16055:301;;16386:110;16459:31;16484:5;16459:31;;16503:107;16574:30;16598:5;16574:30;;16617:289;;16713:34;16741:5;16713:34;;;16764:6;16759:3;16752:19;16776:63;16832:6;16825:4;16820:3;16816:14;16809:4;16802:5;16798:16;16776:63;;;16871:29;16893:6;16871:29;;;16851:50;;;16864:4;16851:50;;16693:213;-1:-1;;;16693:213;16914:397;17069:2;17057:15;;17106:66;17101:2;17092:12;;17085:88;17207:66;17202:2;17193:12;;17186:88;17302:2;17293:12;;17050:261;17320:296;17475:2;17463:15;;17512:66;17507:2;17498:12;;17491:88;17607:2;17598:12;;17456:160;17625:397;17780:2;17768:15;;17817:66;17812:2;17803:12;;17796:88;17918:66;17913:2;17904:12;;17897:88;18013:2;18004:12;;17761:261;18031:397;18186:2;18174:15;;18223:66;18218:2;18209:12;;18202:88;18324:66;18319:2;18310:12;;18303:88;18419:2;18410:12;;18167:261;18497:695;18710:22;;18634:4;18625:14;;;18744:57;18629:3;18710:22;18744:57;;;18654:159;18894:4;18887:5;18883:16;18877:23;18912:62;18968:4;18963:3;18959:14;18946:11;18912:62;;;18823:163;19085:4;19078:5;19074:16;19068:23;19103:62;19159:4;19154:3;19150:14;19137:11;19103:62;;;18996:181;18607:585;;;;20011:2419;20226:22;;20011:2419;;20148:5;20139:15;;;20260:61;20143:3;20226:22;20260:61;;;20169:164;20417:4;20410:5;20406:16;20400:23;20435:62;20491:4;20486:3;20482:14;20469:11;20435:62;;;20343:166;20600:4;20593:5;20589:16;20583:23;20618:62;20674:4;20669:3;20665:14;20652:11;20618:62;;;20519:173;20777:4;20770:5;20766:16;20760:23;20795:62;20851:4;20846:3;20842:14;20829:11;20795:62;;;20702:167;20957:4;20950:5;20946:16;20940:23;20975:62;21031:4;21026:3;21022:14;21009:11;20975:62;;;20879:170;21137:4;21130:5;21126:16;21120:23;21155:62;21211:4;21206:3;21202:14;21189:11;21155:62;;;21059:170;21309:4;21302:5;21298:16;21292:23;21327:62;21383:4;21378:3;21374:14;21361:11;21327:62;;;21239:162;21481:4;21474:5;21470:16;21464:23;21499:62;21555:4;21550:3;21546:14;21533:11;21499:62;;;21411:162;21666:5;21659;21655:17;21649:24;21685:63;21741:5;21736:3;21732:15;21719:11;21685:63;;;21583:177;21836:5;21829;21825:17;21819:24;21855:63;21911:5;21906:3;21902:15;21889:11;21855:63;;;21770:160;22016:5;22009;22005:17;21999:24;22069:3;22063:4;22059:14;22051:5;22046:3;22042:15;22035:39;22089:66;22150:4;22137:11;22089:66;;;22081:74;;21940:227;22253:5;22246;22242:17;22236:24;22306:3;22300:4;22296:14;22288:5;22283:3;22279:15;22272:39;22326:66;22387:4;22374:11;22326:66;;;22318:74;20121:2309;-1:-1;;;;;20121:2309;24987:1587;25200:22;;25122:5;25113:15;;;25234:61;25117:3;25200:22;25234:61;;;25143:164;25393:4;25386:5;25382:16;25376:23;25411:62;25467:4;25462:3;25458:14;25445:11;25411:62;;;25317:168;25569:4;25562:5;25558:16;25552:23;25587:62;25643:4;25638:3;25634:14;25621:11;25587:62;;;25495:166;25747:4;25740:5;25736:16;25730:23;25765:62;25821:4;25816:3;25812:14;25799:11;25765:62;;;25671:168;25926:4;25919:5;25915:16;25909:23;25944:62;26000:4;25995:3;25991:14;25978:11;25944:62;;;25849:169;26107:4;26100:5;26096:16;26090:23;26125:62;26181:4;26176:3;26172:14;26159:11;26125:62;;;26028:171;26286:4;26279:5;26275:16;26269:23;26304:62;26360:4;26355:3;26351:14;26338:11;26304:62;;;26209:169;26467:4;26460:5;26456:16;26450:23;26485:62;26541:4;26536:3;26532:14;26519:11;26485:62;;28363:104;28432:29;28455:5;28432:29;;28474:193;28582:2;28567:18;;28596:61;28571:9;28630:6;28596:61;;28674:294;28810:2;28795:18;;28824:61;28799:9;28858:6;28824:61;;;28896:62;28954:2;28943:9;28939:18;28930:6;28896:62;;28975:770;29301:2;29315:47;;;29286:18;;29376:144;29286:18;29506:6;29376:144;;;29368:152;;29568:9;29562:4;29558:20;29553:2;29542:9;29538:18;29531:48;29593:142;29730:4;29721:6;29593:142;;29752:417;29948:2;29962:47;;;29933:18;;30023:136;29933:18;30145:6;30023:136;;30176:429;30378:2;30392:47;;;30363:18;;30453:142;30363:18;30581:6;30453:142;;30612:189;30718:2;30703:18;;30732:59;30707:9;30764:6;30732:59;;30808:387;30989:2;31003:47;;;30974:18;;31064:121;30974:18;31064:121;;31202:387;31383:2;31397:47;;;31368:18;;31458:121;31368:18;31458:121;;31596:387;31777:2;31791:47;;;31762:18;;31852:121;31762:18;31852:121;;31990:387;32171:2;32185:47;;;32156:18;;32246:121;32156:18;32246:121;;32384:507;32626:3;32611:19;;32641:115;32615:9;32729:6;32641:115;;;32767:114;32877:2;32866:9;32862:18;32853:6;32767:114;;32898:333;33052:2;33066:47;;;33037:18;;33127:94;33037:18;33207:6;33127:94;;33238:298;33398:3;33383:19;;33413:113;33387:9;33499:6;33413:113;;33543:193;33651:2;33636:18;;33665:61;33640:9;33699:6;33665:61;;33743:294;33879:2;33864:18;;33893:61;33868:9;33927:6;33893:61;;;33965:62;34023:2;34012:9;34008:18;33999:6;33965:62;;34044:256;34106:2;34100:9;34132:17;;;34207:18;34192:34;;34228:22;;;34189:62;34186:2;;;34264:1;34261;34254:12;34186:2;34280;34273:22;34084:216;;-1:-1;34084:216;34307:258;;34466:18;34458:6;34455:30;34452:2;;;34498:1;34495;34488:12;34452:2;-1:-1;34527:4;34515:17;;;34545:15;;34389:176;35144:254;;35283:18;35275:6;35272:30;35269:2;;;35315:1;35312;35305:12;35269:2;-1:-1;35388:4;35359;35336:17;;;;35355:9;35332:33;35378:15;;35206:192;35672:144;35804:4;35792:17;;35773:43;36130:130;36243:12;;36227:33;37087:128;37167:42;37156:54;;37139:76;37222:79;37291:5;37274:27;37308:151;37387:66;37376:78;;37359:100;37552:88;37630:4;37619:16;;37602:38;37782:92;37855:13;37848:21;;37831:43;38149:145;38230:6;38225:3;38220;38207:30;-1:-1;38286:1;38268:16;;38261:27;38200:94;38303:268;38368:1;38375:101;38389:6;38386:1;38383:13;38375:101;;;38456:11;;;38450:18;38437:11;;;38430:39;38411:2;38404:10;38375:101;;;38491:6;38488:1;38485:13;38482:2;;;-1:-1;;38556:1;38538:16;;38531:27;38352:219;38579:97;38667:2;38647:14;38663:7;38643:28;;38627:49" + "object": "0x6080604052600436106100825763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166304ad1e5381146100875780632cd0fc73146100be5780634b95de13146100ec578063690d31141461011a578063b698846314610147578063c6b7f4ee14610174578063f241ffb0146101a2575b600080fd5b34801561009357600080fd5b506100a76100a23660046112d3565b6101cf565b6040516100b59291906118dc565b60405180910390f35b3480156100ca57600080fd5b506100de6100d936600461118b565b61029c565b6040516100b5929190611926565b3480156100f857600080fd5b5061010c610107366004611238565b6107cd565b6040516100b5929190611822565b34801561012657600080fd5b5061013a610135366004611238565b6108a4565b6040516100b59190611858565b34801561015357600080fd5b506101676101623660046111d3565b61095e565b6040516100b591906117f9565b34801561018057600080fd5b5061019461018f366004611139565b6109a9565b6040516100b5929190611869565b3480156101ae57600080fd5b506101c26101bd3660046112d3565b610a86565b6040516100b59190611909565b6101d7610cd0565b6101df610cf0565b6000546040517fc75e0a8100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9091169063c75e0a81906102359087906004016118f8565b606060405180830381600087803b15801561024f57600080fd5b505af1158015610263573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061028791908101906112b5565b91506102938484610a86565b90509250929050565b6000808080808080806102b5898263ffffffff610ba416565b95506102c889601063ffffffff610c1116565b6000546040517f6070410800000000000000000000000000000000000000000000000000000000815291965073ffffffffffffffffffffffffffffffffffffffff169063607041089061031f90899060040161188e565b602060405180830381600087803b15801561033957600080fd5b505af115801561034d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506103719190810190611113565b604080517f4552433230546f6b656e28616464726573732900000000000000000000000000815290519081900360130190209094507fffffffff0000000000000000000000000000000000000000000000000000000087811691161415610526576040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8616906370a0823190610424908d906004016117f9565b602060405180830381600087803b15801561043e57600080fd5b505af1158015610452573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610476919081019061131a565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815290985073ffffffffffffffffffffffffffffffffffffffff86169063dd62ed3e906104cd908d908890600401611807565b602060405180830381600087803b1580156104e757600080fd5b505af11580156104fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061051f919081019061131a565b96506107c0565b604080517f455243373231546f6b656e28616464726573732c75696e7432353629000000008152905190819003601c0190207fffffffff00000000000000000000000000000000000000000000000000000000878116911614156107855761059589602463ffffffff610c7216565b92506105a1858461095e565b91508173ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff16146105dd5760006105e0565b60015b60ff1697508473ffffffffffffffffffffffffffffffffffffffff1663e985e9c58b866040518363ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040161063c929190611807565b602060405180830381600087803b15801561065657600080fd5b505af115801561066a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061068e9190810190611297565b8061076a57508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1663081812fc856040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016107009190611918565b602060405180830381600087803b15801561071a57600080fd5b505af115801561072e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506107529190810190611113565b73ffffffffffffffffffffffffffffffffffffffff16145b90508061077857600061077b565b60015b60ff1696506107c0565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b7906118ac565b60405180910390fd5b5050505050509250929050565b6000546040517f7e9d74dc000000000000000000000000000000000000000000000000000000008152606091829173ffffffffffffffffffffffffffffffffffffffff90911690637e9d74dc90610828908790600401611847565b600060405180830381600087803b15801561084257600080fd5b505af1158015610856573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261089c9190810190611203565b915061029384845b606060006060600085519250826040519080825280602002602001820160405280156108ea57816020015b6108d7610cf0565b8152602001906001900390816108cf5790505b509150600090505b80831461095157610931868281518110151561090a57fe5b90602001906020020151868381518110151561092257fe5b90602001906020020151610a86565b828281518110151561093f57fe5b602090810290910101526001016108f2565b8193505b50505092915050565b60006040517f6352211e000000000000000000000000000000000000000000000000000000008152826004820152602081602483875afa80156109a057815192505b50505b92915050565b6060806000606080600086519350836040519080825280602002602001820160405280156109e1578160200160208202803883390190505b50925083604051908082528060200260200182016040528015610a0e578160200160208202803883390190505b509150600090505b808414610a7957610a3e888883815181101515610a2f57fe5b9060200190602002015161029c565b8483815181101515610a4c57fe5b9060200190602002018484815181101515610a6357fe5b6020908102909101019190915252600101610a16565b5090969095509350505050565b610a8e610cf0565b6060610aa3846000015185610140015161029c565b60208401528252610160840151610abb90849061029c565b60608401526040808401919091526001805482516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101008688161502019094169390930492830181900481028201810190945281815292830182828015610b6a5780601f10610b3f57610100808354040283529160200191610b6a565b820191906000526020600020905b815481529060010190602001808311610b4d57829003601f168201915b50505050509050610b7f84600001518261029c565b60a08401526080830152610b93838261029c565b60e084015260c08301525092915050565b600081600401835110151515610be6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b7906118cc565b5050602001517fffffffff000000000000000000000000000000000000000000000000000000001690565b600081601401835110151515610c53576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b7906118bc565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b6000610c7e8383610c85565b9392505050565b600081602001835110151515610cc7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107b79061189c565b50016020015190565b604080516060810182526000808252602082018190529181019190915290565b6101006040519081016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6000610c7e82356119d9565b6000610c7e82516119d9565b6000601f82018313610d5f57600080fd5b8135610d72610d6d82611968565b611941565b91508181835260208401935060208101905083856020840282011115610d9757600080fd5b60005b83811015610dc35781610dad8882610d36565b8452506020928301929190910190600101610d9a565b5050505092915050565b6000601f82018313610dde57600080fd5b8135610dec610d6d82611968565b81815260209384019390925082018360005b83811015610dc35781358601610e148882610f11565b8452506020928301929190910190600101610dfe565b6000601f82018313610e3b57600080fd5b8151610e49610d6d82611968565b91508181835260208401935060208101905083856060840282011115610e6e57600080fd5b60005b83811015610dc35781610e848882610f57565b84525060209092019160609190910190600101610e71565b6000601f82018313610ead57600080fd5b8135610ebb610d6d82611968565b81815260209384019390925082018360005b83811015610dc35781358601610ee38882610fb2565b8452506020928301929190910190600101610ecd565b6000610c7e8251611a20565b6000610c7e82516119f2565b6000601f82018313610f2257600080fd5b8135610f30610d6d82611989565b91508082526020830160208301858383011115610f4c57600080fd5b610955838284611a25565b600060608284031215610f6957600080fd5b610f736060611941565b90506000610f818484611107565b8252506020610f9284848301610f05565b6020830152506040610fa684828501610f05565b60408301525092915050565b60006101808284031215610fc557600080fd5b610fd0610180611941565b90506000610fde8484610d36565b8252506020610fef84848301610d36565b602083015250604061100384828501610d36565b604083015250606061101784828501610d36565b606083015250608061102b848285016110fb565b60808301525060a061103f848285016110fb565b60a08301525060c0611053848285016110fb565b60c08301525060e0611067848285016110fb565b60e08301525061010061107c848285016110fb565b61010083015250610120611092848285016110fb565b6101208301525061014082013567ffffffffffffffff8111156110b457600080fd5b6110c084828501610f11565b6101408301525061016082013567ffffffffffffffff8111156110e257600080fd5b6110ee84828501610f11565b6101608301525092915050565b6000610c7e82356119f2565b6000610c7e8251611a1a565b60006020828403121561112557600080fd5b60006111318484610d42565b949350505050565b6000806040838503121561114c57600080fd5b60006111588585610d36565b925050602083013567ffffffffffffffff81111561117557600080fd5b61118185828601610dcd565b9150509250929050565b6000806040838503121561119e57600080fd5b60006111aa8585610d36565b925050602083013567ffffffffffffffff8111156111c757600080fd5b61118185828601610f11565b600080604083850312156111e657600080fd5b60006111f28585610d36565b9250506020611181858286016110fb565b60006020828403121561121557600080fd5b815167ffffffffffffffff81111561122c57600080fd5b61113184828501610e2a565b6000806040838503121561124b57600080fd5b823567ffffffffffffffff81111561126257600080fd5b61126e85828601610e9c565b925050602083013567ffffffffffffffff81111561128b57600080fd5b61118185828601610d4e565b6000602082840312156112a957600080fd5b60006111318484610ef9565b6000606082840312156112c757600080fd5b60006111318484610f57565b600080604083850312156112e657600080fd5b823567ffffffffffffffff8111156112fd57600080fd5b61130985828601610fb2565b925050602061118185828601610d36565b60006020828403121561132c57600080fd5b60006111318484610f05565b611341816119d9565b82525050565b6000611352826119d5565b808452602084019350611364836119cf565b60005b828110156113945761137a868351611619565b611383826119cf565b606096909601959150600101611367565b5093949350505050565b60006113a9826119d5565b808452602084019350836020820285016113c2856119cf565b60005b848110156113f95783830388526113dd838351611656565b92506113e8826119cf565b6020989098019791506001016113c5565b50909695505050505050565b6000611410826119d5565b808452602084019350611422836119cf565b60005b8281101561139457611438868351611759565b611441826119cf565b61010096909601959150600101611425565b600061145e826119d5565b808452602084019350611470836119cf565b60005b82811015611394576114868683516114a0565b61148f826119cf565b602096909601959150600101611473565b611341816119f2565b611341816119f5565b60006114bd826119d5565b8084526114d1816020860160208601611a31565b6114da81611a5d565b9093016020019392505050565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601781527f554e535550504f525445445f41535345545f50524f5859000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b602581527f475245415445525f4f525f455155414c5f544f5f345f4c454e4754485f52455160208201527f5549524544000000000000000000000000000000000000000000000000000000604082015260600190565b8051606083019061162a84826117f0565b50602082015161163d60208501826114a0565b50604082015161165060408501826114a0565b50505050565b805160009061018084019061166b8582611338565b50602083015161167e6020860182611338565b5060408301516116916040860182611338565b5060608301516116a46060860182611338565b5060808301516116b760808601826114a0565b5060a08301516116ca60a08601826114a0565b5060c08301516116dd60c08601826114a0565b5060e08301516116f060e08601826114a0565b506101008301516117056101008601826114a0565b5061012083015161171a6101208601826114a0565b5061014083015184820361014086015261173482826114b2565b91505061016083015184820361016086015261175082826114b2565b95945050505050565b805161010083019061176b84826114a0565b50602082015161177e60208501826114a0565b50604082015161179160408501826114a0565b5060608201516117a460608501826114a0565b5060808201516117b760808501826114a0565b5060a08201516117ca60a08501826114a0565b5060c08201516117dd60c08501826114a0565b5060e082015161165060e08501826114a0565b61134181611a1a565b602081016109a38284611338565b604081016118158285611338565b610c7e6020830184611338565b604080825281016118338185611347565b905081810360208301526111318184611405565b60208082528101610c7e818461139e565b60208082528101610c7e8184611405565b6040808252810161187a8185611453565b905081810360208301526111318184611453565b602081016109a382846114a9565b602080825281016109a3816114e7565b602080825281016109a38161153d565b602080825281016109a38161156d565b602080825281016109a3816115c3565b61016081016118eb8285611619565b610c7e6060830184611759565b60208082528101610c7e8184611656565b61010081016109a38284611759565b602081016109a382846114a0565b6040810161193482856114a0565b610c7e60208301846114a0565b60405181810167ffffffffffffffff8111828210171561196057600080fd5b604052919050565b600067ffffffffffffffff82111561197f57600080fd5b5060209081020190565b600067ffffffffffffffff8211156119a057600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b60200190565b5190565b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b60ff1690565b151590565b82818337506000910152565b60005b83811015611a4c578181015183820152602001611a34565b838111156116505750506000910152565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016905600a265627a7a72305820d3d495bc287663ed8cb2a513cfad47804ffa6e4ad0cfa6d0d0eadf1f907483586c6578706572696d656e74616cf50037", + "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x82 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x4AD1E53 DUP2 EQ PUSH2 0x87 JUMPI DUP1 PUSH4 0x2CD0FC73 EQ PUSH2 0xBE JUMPI DUP1 PUSH4 0x4B95DE13 EQ PUSH2 0xEC JUMPI DUP1 PUSH4 0x690D3114 EQ PUSH2 0x11A JUMPI DUP1 PUSH4 0xB6988463 EQ PUSH2 0x147 JUMPI DUP1 PUSH4 0xC6B7F4EE EQ PUSH2 0x174 JUMPI DUP1 PUSH4 0xF241FFB0 EQ PUSH2 0x1A2 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x93 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xA7 PUSH2 0xA2 CALLDATASIZE PUSH1 0x4 PUSH2 0x12D3 JUMP JUMPDEST PUSH2 0x1CF JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xB5 SWAP3 SWAP2 SWAP1 PUSH2 0x18DC JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xCA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xDE PUSH2 0xD9 CALLDATASIZE PUSH1 0x4 PUSH2 0x118B JUMP JUMPDEST PUSH2 0x29C JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xB5 SWAP3 SWAP2 SWAP1 PUSH2 0x1926 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xF8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x10C PUSH2 0x107 CALLDATASIZE PUSH1 0x4 PUSH2 0x1238 JUMP JUMPDEST PUSH2 0x7CD JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xB5 SWAP3 SWAP2 SWAP1 PUSH2 0x1822 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x126 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x13A PUSH2 0x135 CALLDATASIZE PUSH1 0x4 PUSH2 0x1238 JUMP JUMPDEST PUSH2 0x8A4 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xB5 SWAP2 SWAP1 PUSH2 0x1858 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x153 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x167 PUSH2 0x162 CALLDATASIZE PUSH1 0x4 PUSH2 0x11D3 JUMP JUMPDEST PUSH2 0x95E JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xB5 SWAP2 SWAP1 PUSH2 0x17F9 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x180 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x194 PUSH2 0x18F CALLDATASIZE PUSH1 0x4 PUSH2 0x1139 JUMP JUMPDEST PUSH2 0x9A9 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xB5 SWAP3 SWAP2 SWAP1 PUSH2 0x1869 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1AE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1C2 PUSH2 0x1BD CALLDATASIZE PUSH1 0x4 PUSH2 0x12D3 JUMP JUMPDEST PUSH2 0xA86 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0xB5 SWAP2 SWAP1 PUSH2 0x1909 JUMP JUMPDEST PUSH2 0x1D7 PUSH2 0xCD0 JUMP JUMPDEST PUSH2 0x1DF PUSH2 0xCF0 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x40 MLOAD PUSH32 0xC75E0A8100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP2 AND SWAP1 PUSH4 0xC75E0A81 SWAP1 PUSH2 0x235 SWAP1 DUP8 SWAP1 PUSH1 0x4 ADD PUSH2 0x18F8 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x24F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x263 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x287 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x12B5 JUMP JUMPDEST SWAP2 POP PUSH2 0x293 DUP5 DUP5 PUSH2 0xA86 JUMP JUMPDEST SWAP1 POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP1 DUP1 DUP1 DUP1 DUP1 DUP1 PUSH2 0x2B5 DUP10 DUP3 PUSH4 0xFFFFFFFF PUSH2 0xBA4 AND JUMP JUMPDEST SWAP6 POP PUSH2 0x2C8 DUP10 PUSH1 0x10 PUSH4 0xFFFFFFFF PUSH2 0xC11 AND JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x40 MLOAD PUSH32 0x6070410800000000000000000000000000000000000000000000000000000000 DUP2 MSTORE SWAP2 SWAP7 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 PUSH4 0x60704108 SWAP1 PUSH2 0x31F SWAP1 DUP10 SWAP1 PUSH1 0x4 ADD PUSH2 0x188E JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x339 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x34D JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x371 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x1113 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x4552433230546F6B656E28616464726573732900000000000000000000000000 DUP2 MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x13 ADD SWAP1 KECCAK256 SWAP1 SWAP5 POP PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP8 DUP2 AND SWAP2 AND EQ ISZERO PUSH2 0x526 JUMPI PUSH1 0x40 MLOAD PUSH32 0x70A0823100000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND SWAP1 PUSH4 0x70A08231 SWAP1 PUSH2 0x424 SWAP1 DUP14 SWAP1 PUSH1 0x4 ADD PUSH2 0x17F9 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x43E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x452 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x476 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x131A JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0xDD62ED3E00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE SWAP1 SWAP9 POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP7 AND SWAP1 PUSH4 0xDD62ED3E SWAP1 PUSH2 0x4CD SWAP1 DUP14 SWAP1 DUP9 SWAP1 PUSH1 0x4 ADD PUSH2 0x1807 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x4E7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x4FB JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x51F SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x131A JUMP JUMPDEST SWAP7 POP PUSH2 0x7C0 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH32 0x455243373231546F6B656E28616464726573732C75696E743235362900000000 DUP2 MSTORE SWAP1 MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x1C ADD SWAP1 KECCAK256 PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 DUP8 DUP2 AND SWAP2 AND EQ ISZERO PUSH2 0x785 JUMPI PUSH2 0x595 DUP10 PUSH1 0x24 PUSH4 0xFFFFFFFF PUSH2 0xC72 AND JUMP JUMPDEST SWAP3 POP PUSH2 0x5A1 DUP6 DUP5 PUSH2 0x95E JUMP JUMPDEST SWAP2 POP DUP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP11 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ PUSH2 0x5DD JUMPI PUSH1 0x0 PUSH2 0x5E0 JUMP JUMPDEST PUSH1 0x1 JUMPDEST PUSH1 0xFF AND SWAP8 POP DUP5 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0xE985E9C5 DUP12 DUP7 PUSH1 0x40 MLOAD DUP4 PUSH4 0xFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x63C SWAP3 SWAP2 SWAP1 PUSH2 0x1807 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x656 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x66A JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x68E SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x1297 JUMP JUMPDEST DUP1 PUSH2 0x76A JUMPI POP DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH4 0x81812FC DUP6 PUSH1 0x40 MLOAD DUP3 PUSH4 0xFFFFFFFF AND PUSH29 0x100000000000000000000000000000000000000000000000000000000 MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x700 SWAP2 SWAP1 PUSH2 0x1918 JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x71A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x72E JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x1F NOT PUSH1 0x1F DUP3 ADD AND DUP3 ADD DUP1 PUSH1 0x40 MSTORE POP PUSH2 0x752 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x1113 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND EQ JUMPDEST SWAP1 POP DUP1 PUSH2 0x778 JUMPI PUSH1 0x0 PUSH2 0x77B JUMP JUMPDEST PUSH1 0x1 JUMPDEST PUSH1 0xFF AND SWAP7 POP PUSH2 0x7C0 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x7B7 SWAP1 PUSH2 0x18AC JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST POP POP POP POP POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x40 MLOAD PUSH32 0x7E9D74DC00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x60 SWAP2 DUP3 SWAP2 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF SWAP1 SWAP2 AND SWAP1 PUSH4 0x7E9D74DC SWAP1 PUSH2 0x828 SWAP1 DUP8 SWAP1 PUSH1 0x4 ADD PUSH2 0x1847 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0x842 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0x856 JUMPI RETURNDATASIZE PUSH1 0x0 DUP1 RETURNDATACOPY RETURNDATASIZE PUSH1 0x0 REVERT JUMPDEST POP POP POP POP PUSH1 0x40 MLOAD RETURNDATASIZE PUSH1 0x0 DUP3 RETURNDATACOPY PUSH1 0x1F RETURNDATASIZE SWAP1 DUP2 ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND DUP3 ADD PUSH1 0x40 MSTORE PUSH2 0x89C SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0x1203 JUMP JUMPDEST SWAP2 POP PUSH2 0x293 DUP5 DUP5 JUMPDEST PUSH1 0x60 PUSH1 0x0 PUSH1 0x60 PUSH1 0x0 DUP6 MLOAD SWAP3 POP DUP3 PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0x8EA JUMPI DUP2 PUSH1 0x20 ADD JUMPDEST PUSH2 0x8D7 PUSH2 0xCF0 JUMP JUMPDEST DUP2 MSTORE PUSH1 0x20 ADD SWAP1 PUSH1 0x1 SWAP1 SUB SWAP1 DUP2 PUSH2 0x8CF JUMPI SWAP1 POP JUMPDEST POP SWAP2 POP PUSH1 0x0 SWAP1 POP JUMPDEST DUP1 DUP4 EQ PUSH2 0x951 JUMPI PUSH2 0x931 DUP7 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x90A JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP7 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x922 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0xA86 JUMP JUMPDEST DUP3 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0x93F JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MSTORE PUSH1 0x1 ADD PUSH2 0x8F2 JUMP JUMPDEST DUP2 SWAP4 POP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x40 MLOAD PUSH32 0x6352211E00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE DUP3 PUSH1 0x4 DUP3 ADD MSTORE PUSH1 0x20 DUP2 PUSH1 0x24 DUP4 DUP8 GAS STATICCALL DUP1 ISZERO PUSH2 0x9A0 JUMPI DUP2 MLOAD SWAP3 POP JUMPDEST POP POP JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x60 DUP1 PUSH1 0x0 PUSH1 0x60 DUP1 PUSH1 0x0 DUP7 MLOAD SWAP4 POP DUP4 PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0x9E1 JUMPI DUP2 PUSH1 0x20 ADD PUSH1 0x20 DUP3 MUL DUP1 CODESIZE DUP4 CODECOPY ADD SWAP1 POP JUMPDEST POP SWAP3 POP DUP4 PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0xA0E JUMPI DUP2 PUSH1 0x20 ADD PUSH1 0x20 DUP3 MUL DUP1 CODESIZE DUP4 CODECOPY ADD SWAP1 POP JUMPDEST POP SWAP2 POP PUSH1 0x0 SWAP1 POP JUMPDEST DUP1 DUP5 EQ PUSH2 0xA79 JUMPI PUSH2 0xA3E DUP9 DUP9 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xA2F JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH2 0x29C JUMP JUMPDEST DUP5 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xA4C JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD DUP5 DUP5 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xA63 JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD SWAP2 SWAP1 SWAP2 MSTORE MSTORE PUSH1 0x1 ADD PUSH2 0xA16 JUMP JUMPDEST POP SWAP1 SWAP7 SWAP1 SWAP6 POP SWAP4 POP POP POP POP JUMP JUMPDEST PUSH2 0xA8E PUSH2 0xCF0 JUMP JUMPDEST PUSH1 0x60 PUSH2 0xAA3 DUP5 PUSH1 0x0 ADD MLOAD DUP6 PUSH2 0x140 ADD MLOAD PUSH2 0x29C JUMP JUMPDEST PUSH1 0x20 DUP5 ADD MSTORE DUP3 MSTORE PUSH2 0x160 DUP5 ADD MLOAD PUSH2 0xABB SWAP1 DUP5 SWAP1 PUSH2 0x29C JUMP JUMPDEST PUSH1 0x60 DUP5 ADD MSTORE PUSH1 0x40 DUP1 DUP5 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x1 DUP1 SLOAD DUP3 MLOAD PUSH1 0x20 PUSH1 0x1F PUSH1 0x2 PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH2 0x100 DUP7 DUP9 AND ISZERO MUL ADD SWAP1 SWAP5 AND SWAP4 SWAP1 SWAP4 DIV SWAP3 DUP4 ADD DUP2 SWAP1 DIV DUP2 MUL DUP3 ADD DUP2 ADD SWAP1 SWAP5 MSTORE DUP2 DUP2 MSTORE SWAP3 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0xB6A JUMPI DUP1 PUSH1 0x1F LT PUSH2 0xB3F JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0xB6A JUMP JUMPDEST DUP3 ADD SWAP2 SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD DUP2 MSTORE SWAP1 PUSH1 0x1 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0xB4D JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP POP POP SWAP1 POP PUSH2 0xB7F DUP5 PUSH1 0x0 ADD MLOAD DUP3 PUSH2 0x29C JUMP JUMPDEST PUSH1 0xA0 DUP5 ADD MSTORE PUSH1 0x80 DUP4 ADD MSTORE PUSH2 0xB93 DUP4 DUP3 PUSH2 0x29C JUMP JUMPDEST PUSH1 0xE0 DUP5 ADD MSTORE PUSH1 0xC0 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x4 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0xBE6 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x7B7 SWAP1 PUSH2 0x18CC JUMP JUMPDEST POP POP PUSH1 0x20 ADD MLOAD PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x14 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0xC53 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x7B7 SWAP1 PUSH2 0x18BC JUMP JUMPDEST POP ADD PUSH1 0x14 ADD MLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC7E DUP4 DUP4 PUSH2 0xC85 JUMP JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 PUSH1 0x20 ADD DUP4 MLOAD LT ISZERO ISZERO ISZERO PUSH2 0xCC7 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x7B7 SWAP1 PUSH2 0x189C JUMP JUMPDEST POP ADD PUSH1 0x20 ADD MLOAD SWAP1 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x60 DUP2 ADD DUP3 MSTORE PUSH1 0x0 DUP1 DUP3 MSTORE PUSH1 0x20 DUP3 ADD DUP2 SWAP1 MSTORE SWAP2 DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE SWAP1 JUMP JUMPDEST PUSH2 0x100 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 DUP2 MSTORE POP SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC7E DUP3 CALLDATALOAD PUSH2 0x19D9 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC7E DUP3 MLOAD PUSH2 0x19D9 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xD5F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xD72 PUSH2 0xD6D DUP3 PUSH2 0x1968 JUMP JUMPDEST PUSH2 0x1941 JUMP JUMPDEST SWAP2 POP DUP2 DUP2 DUP4 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH1 0x20 DUP2 ADD SWAP1 POP DUP4 DUP6 PUSH1 0x20 DUP5 MUL DUP3 ADD GT ISZERO PUSH2 0xD97 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xDC3 JUMPI DUP2 PUSH2 0xDAD DUP9 DUP3 PUSH2 0xD36 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0xD9A JUMP JUMPDEST POP POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xDDE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xDEC PUSH2 0xD6D DUP3 PUSH2 0x1968 JUMP JUMPDEST DUP2 DUP2 MSTORE PUSH1 0x20 SWAP4 DUP5 ADD SWAP4 SWAP1 SWAP3 POP DUP3 ADD DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xDC3 JUMPI DUP2 CALLDATALOAD DUP7 ADD PUSH2 0xE14 DUP9 DUP3 PUSH2 0xF11 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0xDFE JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xE3B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 MLOAD PUSH2 0xE49 PUSH2 0xD6D DUP3 PUSH2 0x1968 JUMP JUMPDEST SWAP2 POP DUP2 DUP2 DUP4 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH1 0x20 DUP2 ADD SWAP1 POP DUP4 DUP6 PUSH1 0x60 DUP5 MUL DUP3 ADD GT ISZERO PUSH2 0xE6E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xDC3 JUMPI DUP2 PUSH2 0xE84 DUP9 DUP3 PUSH2 0xF57 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x60 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0xE71 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xEAD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xEBB PUSH2 0xD6D DUP3 PUSH2 0x1968 JUMP JUMPDEST DUP2 DUP2 MSTORE PUSH1 0x20 SWAP4 DUP5 ADD SWAP4 SWAP1 SWAP3 POP DUP3 ADD DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xDC3 JUMPI DUP2 CALLDATALOAD DUP7 ADD PUSH2 0xEE3 DUP9 DUP3 PUSH2 0xFB2 JUMP JUMPDEST DUP5 MSTORE POP PUSH1 0x20 SWAP3 DUP4 ADD SWAP3 SWAP2 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x1 ADD PUSH2 0xECD JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC7E DUP3 MLOAD PUSH2 0x1A20 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC7E DUP3 MLOAD PUSH2 0x19F2 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xF22 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xF30 PUSH2 0xD6D DUP3 PUSH2 0x1989 JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH2 0xF4C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x955 DUP4 DUP3 DUP5 PUSH2 0x1A25 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x60 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xF69 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xF73 PUSH1 0x60 PUSH2 0x1941 JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0xF81 DUP5 DUP5 PUSH2 0x1107 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0xF92 DUP5 DUP5 DUP4 ADD PUSH2 0xF05 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0xFA6 DUP5 DUP3 DUP6 ADD PUSH2 0xF05 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x180 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xFC5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xFD0 PUSH2 0x180 PUSH2 0x1941 JUMP JUMPDEST SWAP1 POP PUSH1 0x0 PUSH2 0xFDE DUP5 DUP5 PUSH2 0xD36 JUMP JUMPDEST DUP3 MSTORE POP PUSH1 0x20 PUSH2 0xFEF DUP5 DUP5 DUP4 ADD PUSH2 0xD36 JUMP JUMPDEST PUSH1 0x20 DUP4 ADD MSTORE POP PUSH1 0x40 PUSH2 0x1003 DUP5 DUP3 DUP6 ADD PUSH2 0xD36 JUMP JUMPDEST PUSH1 0x40 DUP4 ADD MSTORE POP PUSH1 0x60 PUSH2 0x1017 DUP5 DUP3 DUP6 ADD PUSH2 0xD36 JUMP JUMPDEST PUSH1 0x60 DUP4 ADD MSTORE POP PUSH1 0x80 PUSH2 0x102B DUP5 DUP3 DUP6 ADD PUSH2 0x10FB JUMP JUMPDEST PUSH1 0x80 DUP4 ADD MSTORE POP PUSH1 0xA0 PUSH2 0x103F DUP5 DUP3 DUP6 ADD PUSH2 0x10FB JUMP JUMPDEST PUSH1 0xA0 DUP4 ADD MSTORE POP PUSH1 0xC0 PUSH2 0x1053 DUP5 DUP3 DUP6 ADD PUSH2 0x10FB JUMP JUMPDEST PUSH1 0xC0 DUP4 ADD MSTORE POP PUSH1 0xE0 PUSH2 0x1067 DUP5 DUP3 DUP6 ADD PUSH2 0x10FB JUMP JUMPDEST PUSH1 0xE0 DUP4 ADD MSTORE POP PUSH2 0x100 PUSH2 0x107C DUP5 DUP3 DUP6 ADD PUSH2 0x10FB JUMP JUMPDEST PUSH2 0x100 DUP4 ADD MSTORE POP PUSH2 0x120 PUSH2 0x1092 DUP5 DUP3 DUP6 ADD PUSH2 0x10FB JUMP JUMPDEST PUSH2 0x120 DUP4 ADD MSTORE POP PUSH2 0x140 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x10B4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x10C0 DUP5 DUP3 DUP6 ADD PUSH2 0xF11 JUMP JUMPDEST PUSH2 0x140 DUP4 ADD MSTORE POP PUSH2 0x160 DUP3 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x10E2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x10EE DUP5 DUP3 DUP6 ADD PUSH2 0xF11 JUMP JUMPDEST PUSH2 0x160 DUP4 ADD MSTORE POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC7E DUP3 CALLDATALOAD PUSH2 0x19F2 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC7E DUP3 MLOAD PUSH2 0x1A1A JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x1125 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x1131 DUP5 DUP5 PUSH2 0xD42 JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x114C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x1158 DUP6 DUP6 PUSH2 0xD36 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 DUP4 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x1175 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x1181 DUP6 DUP3 DUP7 ADD PUSH2 0xDCD JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x119E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x11AA DUP6 DUP6 PUSH2 0xD36 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 DUP4 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x11C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x1181 DUP6 DUP3 DUP7 ADD PUSH2 0xF11 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x11E6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x11F2 DUP6 DUP6 PUSH2 0xD36 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x1181 DUP6 DUP3 DUP7 ADD PUSH2 0x10FB JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x1215 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 MLOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x122C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x1131 DUP5 DUP3 DUP6 ADD PUSH2 0xE2A JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x124B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP3 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x1262 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x126E DUP6 DUP3 DUP7 ADD PUSH2 0xE9C JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 DUP4 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x128B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x1181 DUP6 DUP3 DUP7 ADD PUSH2 0xD4E JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x12A9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x1131 DUP5 DUP5 PUSH2 0xEF9 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x60 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x12C7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x1131 DUP5 DUP5 PUSH2 0xF57 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0x12E6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP3 CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0x12FD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x1309 DUP6 DUP3 DUP7 ADD PUSH2 0xFB2 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0x1181 DUP6 DUP3 DUP7 ADD PUSH2 0xD36 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0x132C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x1131 DUP5 DUP5 PUSH2 0xF05 JUMP JUMPDEST PUSH2 0x1341 DUP2 PUSH2 0x19D9 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x1352 DUP3 PUSH2 0x19D5 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH2 0x1364 DUP4 PUSH2 0x19CF JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x1394 JUMPI PUSH2 0x137A DUP7 DUP4 MLOAD PUSH2 0x1619 JUMP JUMPDEST PUSH2 0x1383 DUP3 PUSH2 0x19CF JUMP JUMPDEST PUSH1 0x60 SWAP7 SWAP1 SWAP7 ADD SWAP6 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x1367 JUMP JUMPDEST POP SWAP4 SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x13A9 DUP3 PUSH2 0x19D5 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP DUP4 PUSH1 0x20 DUP3 MUL DUP6 ADD PUSH2 0x13C2 DUP6 PUSH2 0x19CF JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP5 DUP2 LT ISZERO PUSH2 0x13F9 JUMPI DUP4 DUP4 SUB DUP9 MSTORE PUSH2 0x13DD DUP4 DUP4 MLOAD PUSH2 0x1656 JUMP JUMPDEST SWAP3 POP PUSH2 0x13E8 DUP3 PUSH2 0x19CF JUMP JUMPDEST PUSH1 0x20 SWAP9 SWAP1 SWAP9 ADD SWAP8 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x13C5 JUMP JUMPDEST POP SWAP1 SWAP7 SWAP6 POP POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0x1410 DUP3 PUSH2 0x19D5 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH2 0x1422 DUP4 PUSH2 0x19CF JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x1394 JUMPI PUSH2 0x1438 DUP7 DUP4 MLOAD PUSH2 0x1759 JUMP JUMPDEST PUSH2 0x1441 DUP3 PUSH2 0x19CF JUMP JUMPDEST PUSH2 0x100 SWAP7 SWAP1 SWAP7 ADD SWAP6 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x1425 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x145E DUP3 PUSH2 0x19D5 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH1 0x20 DUP5 ADD SWAP4 POP PUSH2 0x1470 DUP4 PUSH2 0x19CF JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP3 DUP2 LT ISZERO PUSH2 0x1394 JUMPI PUSH2 0x1486 DUP7 DUP4 MLOAD PUSH2 0x14A0 JUMP JUMPDEST PUSH2 0x148F DUP3 PUSH2 0x19CF JUMP JUMPDEST PUSH1 0x20 SWAP7 SWAP1 SWAP7 ADD SWAP6 SWAP2 POP PUSH1 0x1 ADD PUSH2 0x1473 JUMP JUMPDEST PUSH2 0x1341 DUP2 PUSH2 0x19F2 JUMP JUMPDEST PUSH2 0x1341 DUP2 PUSH2 0x19F5 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x14BD DUP3 PUSH2 0x19D5 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH2 0x14D1 DUP2 PUSH1 0x20 DUP7 ADD PUSH1 0x20 DUP7 ADD PUSH2 0x1A31 JUMP JUMPDEST PUSH2 0x14DA DUP2 PUSH2 0x1A5D JUMP JUMPDEST SWAP1 SWAP4 ADD PUSH1 0x20 ADD SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F33325F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x17 DUP2 MSTORE PUSH32 0x554E535550504F525445445F41535345545F50524F5859000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE PUSH1 0x40 ADD SWAP1 JUMP JUMPDEST PUSH1 0x26 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F32305F4C454E4754485F5245 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5155495245440000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x25 DUP2 MSTORE PUSH32 0x475245415445525F4F525F455155414C5F544F5F345F4C454E4754485F524551 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x5549524544000000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST DUP1 MLOAD PUSH1 0x60 DUP4 ADD SWAP1 PUSH2 0x162A DUP5 DUP3 PUSH2 0x17F0 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x163D PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x1650 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP POP POP POP JUMP JUMPDEST DUP1 MLOAD PUSH1 0x0 SWAP1 PUSH2 0x180 DUP5 ADD SWAP1 PUSH2 0x166B DUP6 DUP3 PUSH2 0x1338 JUMP JUMPDEST POP PUSH1 0x20 DUP4 ADD MLOAD PUSH2 0x167E PUSH1 0x20 DUP7 ADD DUP3 PUSH2 0x1338 JUMP JUMPDEST POP PUSH1 0x40 DUP4 ADD MLOAD PUSH2 0x1691 PUSH1 0x40 DUP7 ADD DUP3 PUSH2 0x1338 JUMP JUMPDEST POP PUSH1 0x60 DUP4 ADD MLOAD PUSH2 0x16A4 PUSH1 0x60 DUP7 ADD DUP3 PUSH2 0x1338 JUMP JUMPDEST POP PUSH1 0x80 DUP4 ADD MLOAD PUSH2 0x16B7 PUSH1 0x80 DUP7 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0xA0 DUP4 ADD MLOAD PUSH2 0x16CA PUSH1 0xA0 DUP7 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0xC0 DUP4 ADD MLOAD PUSH2 0x16DD PUSH1 0xC0 DUP7 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0xE0 DUP4 ADD MLOAD PUSH2 0x16F0 PUSH1 0xE0 DUP7 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH2 0x100 DUP4 ADD MLOAD PUSH2 0x1705 PUSH2 0x100 DUP7 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH2 0x120 DUP4 ADD MLOAD PUSH2 0x171A PUSH2 0x120 DUP7 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH2 0x140 DUP4 ADD MLOAD DUP5 DUP3 SUB PUSH2 0x140 DUP7 ADD MSTORE PUSH2 0x1734 DUP3 DUP3 PUSH2 0x14B2 JUMP JUMPDEST SWAP2 POP POP PUSH2 0x160 DUP4 ADD MLOAD DUP5 DUP3 SUB PUSH2 0x160 DUP7 ADD MSTORE PUSH2 0x1750 DUP3 DUP3 PUSH2 0x14B2 JUMP JUMPDEST SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST DUP1 MLOAD PUSH2 0x100 DUP4 ADD SWAP1 PUSH2 0x176B DUP5 DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0x20 DUP3 ADD MLOAD PUSH2 0x177E PUSH1 0x20 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0x40 DUP3 ADD MLOAD PUSH2 0x1791 PUSH1 0x40 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0x60 DUP3 ADD MLOAD PUSH2 0x17A4 PUSH1 0x60 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0x80 DUP3 ADD MLOAD PUSH2 0x17B7 PUSH1 0x80 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0xA0 DUP3 ADD MLOAD PUSH2 0x17CA PUSH1 0xA0 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0xC0 DUP3 ADD MLOAD PUSH2 0x17DD PUSH1 0xC0 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST POP PUSH1 0xE0 DUP3 ADD MLOAD PUSH2 0x1650 PUSH1 0xE0 DUP6 ADD DUP3 PUSH2 0x14A0 JUMP JUMPDEST PUSH2 0x1341 DUP2 PUSH2 0x1A1A JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x9A3 DUP3 DUP5 PUSH2 0x1338 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x1815 DUP3 DUP6 PUSH2 0x1338 JUMP JUMPDEST PUSH2 0xC7E PUSH1 0x20 DUP4 ADD DUP5 PUSH2 0x1338 JUMP JUMPDEST PUSH1 0x40 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x1833 DUP2 DUP6 PUSH2 0x1347 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0x1131 DUP2 DUP5 PUSH2 0x1405 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xC7E DUP2 DUP5 PUSH2 0x139E JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xC7E DUP2 DUP5 PUSH2 0x1405 JUMP JUMPDEST PUSH1 0x40 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x187A DUP2 DUP6 PUSH2 0x1453 JUMP JUMPDEST SWAP1 POP DUP2 DUP2 SUB PUSH1 0x20 DUP4 ADD MSTORE PUSH2 0x1131 DUP2 DUP5 PUSH2 0x1453 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x9A3 DUP3 DUP5 PUSH2 0x14A9 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x9A3 DUP2 PUSH2 0x14E7 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x9A3 DUP2 PUSH2 0x153D JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x9A3 DUP2 PUSH2 0x156D JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x9A3 DUP2 PUSH2 0x15C3 JUMP JUMPDEST PUSH2 0x160 DUP2 ADD PUSH2 0x18EB DUP3 DUP6 PUSH2 0x1619 JUMP JUMPDEST PUSH2 0xC7E PUSH1 0x60 DUP4 ADD DUP5 PUSH2 0x1759 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xC7E DUP2 DUP5 PUSH2 0x1656 JUMP JUMPDEST PUSH2 0x100 DUP2 ADD PUSH2 0x9A3 DUP3 DUP5 PUSH2 0x1759 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x9A3 DUP3 DUP5 PUSH2 0x14A0 JUMP JUMPDEST PUSH1 0x40 DUP2 ADD PUSH2 0x1934 DUP3 DUP6 PUSH2 0x14A0 JUMP JUMPDEST PUSH2 0xC7E PUSH1 0x20 DUP4 ADD DUP5 PUSH2 0x14A0 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0x1960 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x197F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 SWAP1 DUP2 MUL ADD SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x19A0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND ADD SWAP1 JUMP JUMPDEST PUSH1 0x20 ADD SWAP1 JUMP JUMPDEST MLOAD SWAP1 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000 AND SWAP1 JUMP JUMPDEST PUSH1 0xFF AND SWAP1 JUMP JUMPDEST ISZERO ISZERO SWAP1 JUMP JUMPDEST DUP3 DUP2 DUP4 CALLDATACOPY POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x1A4C JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x1A34 JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0x1650 JUMPI POP POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x1F ADD PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 AND SWAP1 JUMP STOP LOG2 PUSH6 0x627A7A723058 KECCAK256 0xd3 0xd4 SWAP6 0xbc 0x28 PUSH23 0x63ED8CB2A513CFAD47804FFA6E4AD0CFA6D0D0EADF1F90 PUSH21 0x83586C6578706572696D656E74616CF50037000000 ", + "sourceMap": "898:8536:11:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2336:352;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;2336:352:11;;;;;;;;;;;;;;;;;;;;;;;;;;5614:1359;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;5614:1359:11;;;;;;;;;;;;;;;;;;3018:384;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3018:384:11;;;;;;;;;;;;;;;;;;4716:452;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4716:452:11;;;;;;;;;;;;;;;;;8218:1214;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;8218:1214:11;;;;;;;;;;;;;;;;;7416:517;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;7416:517:11;;;;;;;;;;;;;;;;;;3661:739;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3661:739:11;;;;;;;;;;;;;;;;;2336:352;2463:35;;:::i;:::-;2500:28;;:::i;:::-;2556:8;;:28;;;;;:8;;;;;:21;;:28;;2578:5;;2556:28;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2556:28:11;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;2556:28:11;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;2556:28:11;;;;;;;;;2544:40;;2607:34;2621:5;2628:12;2607:13;:34::i;:::-;2594:47;-1:-1:-1;2336:352:11;;;;;:::o;5614:1359::-;5731:15;;;;;;;;5803:23;:9;5731:15;5803:23;:20;:23;:::i;:::-;5781:45;-1:-1:-1;5852:25:11;:9;5874:2;5852:25;:21;:25;:::i;:::-;5908:8;;:36;;;;;5836:41;;-1:-1:-1;5908:8:11;;;:22;;:36;;5931:12;;5908:36;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;5908:36:11;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;5908:36:11;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;5908:36:11;;;;;;;;;977:32;;;;;;;;;;;;;;;;5887:57;;-1:-1:-1;5959:29:11;;;;;;;5955:975;;;6043:36;;;;;:28;;;;;;:36;;6072:6;;6043:36;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;6043:36:11;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;6043:36:11;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;6043:36:11;;;;;;;;;6137:48;;;;;6033:46;;-1:-1:-1;6137:28:11;;;;;;:48;;6166:6;;6174:10;;6137:48;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;6137:48:11;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;6137:48:11;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;6137:48:11;;;;;;;;;6125:60;;5955:975;;;1065:41;;;;;;;;;;;;;;;;6206:30;;;;;;;6202:728;;;6270:25;:9;6292:2;6270:25;:21;:25;:::i;:::-;6252:43;;6364:35;6384:5;6391:7;6364:19;:35::i;:::-;6348:51;;6496:5;6486:15;;:6;:15;;;:23;;6508:1;6486:23;;;6504:1;6486:23;6476:33;;;;6620:5;6607:36;;;6644:6;6652:10;6607:56;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;6607:56:11;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;6607:56:11;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;6607:56:11;;;;;;;;;:114;;;;6711:10;6667:54;;6680:5;6667:31;;;6699:7;6667:40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;6667:40:11;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;6667:40:11;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;6667:40:11;;;;;;;;;:54;;;6607:114;6589:132;;6837:10;:18;;6854:1;6837:18;;;6850:1;6837:18;6825:30;;;;6202:728;;;6886:33;;;;;;;;;;;;;;;;;;;6202:728;5614:1359;;;;;;;;;;;:::o;3018:384::-;3261:8;;:30;;;;;3161:38;;;;3261:8;;;;;:22;;:30;;3284:6;;3261:30;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3261:30:11;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3261:30:11;;;;;;39:16:-1;36:1;17:17;2:54;101:4;3261:30:11;80:15:-1;;;97:9;76:31;65:43;;120:4;113:20;3261:30:11;;;;;;;;;3248:43;;3315:38;3330:6;3338:14;4716:452;4850:12;4885:20;4931:31;5010:9;4908:6;:13;4885:36;;4982:12;4965:30;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;4931:64;;5022:1;5010:13;;5005:129;5025:17;;;5005:129;;5080:43;5094:6;5101:1;5094:9;;;;;;;;;;;;;;;;;;5105:14;5120:1;5105:17;;;;;;;;;;;;;;;;;;5080:13;:43::i;:::-;5063:11;5075:1;5063:14;;;;;;;;;;;;;;;;;;:60;5044:3;;5005:129;;;5150:11;5143:18;;4716:452;;;;;;;;:::o;8218:1214::-;8324:13;8437:2;8431:9;8534:66;8525:7;8518:83;8638:7;8634:1;8625:7;8621:15;8614:32;9060:2;9007:7;8950:2;8903:7;8854:5;8809:3;8781:332;9240:7;9237:2;;;9281:7;9275:14;9266:23;;9237:2;-1:-1:-1;;8218:1214:11;;;;;:::o;7416:517::-;7537:9;7555;7587:14;7630:25;7689:27;7755:9;7604;:16;7587:33;;7672:6;7658:21;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;7658:21:11;;7630:49;;7733:6;7719:21;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;7719:21:11;;7689:51;;7767:1;7755:13;;7750:138;7770:11;;;7750:138;;7833:44;7856:6;7864:9;7874:1;7864:12;;;;;;;;;;;;;;;;;;7833:22;:44::i;:::-;7803:8;7812:1;7803:11;;;;;;;;;;;;;;;;;7816:10;7827:1;7816:13;;;;;;;;;;;;;;;;;;7802:75;;;;;7783:3;;7750:138;;;-1:-1:-1;7905:8:11;;7915:10;;-1:-1:-1;7416:517:11;-1:-1:-1;;;;7416:517:11:o;3661:739::-;3780:28;;:::i;:::-;4076:25;3879:64;3902:5;:18;;;3922:5;:20;;;3879:22;:64::i;:::-;3850:25;;;3824:119;;;4045:20;;;;4008:58;;4031:12;;4008:22;:58::i;:::-;3979:25;;;3953:113;3954:23;;;;3953:113;;;;4104:14;4076:42;;;;-1:-1:-1;4076:42:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4104:14;4076:42;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4189:56;4212:5;:18;;;4232:12;4189:22;:56::i;:::-;4157:28;;;4128:117;4129:26;;;4128:117;4316:50;4339:12;4353;4316:22;:50::i;:::-;4284:28;;;4255:111;4256:26;;;4255:111;3661:739;;;;;:::o;15559:559:53:-;15679:13;15741:5;15749:1;15741:9;15729:1;:8;:21;;15708:105;;;;;;;;;;;;;;;;-1:-1:-1;;15869:2:53;15862:10;15856:17;16012:66;16000:79;;15559:559::o;10259:886::-;10380:14;10443:5;10451:2;10443:10;10431:1;:8;:22;;10410:135;;;;;;;;;;;;;;;;-1:-1:-1;11047:13:53;10792:2;11047:13;11041:20;11063:42;11037:69;;10259:886::o;14699:195::-;14820:14;14865:21;14877:1;14880:5;14865:11;:21::i;:::-;14857:30;14699:195;-1:-1:-1;;;14699:195:53:o;13281:490::-;13402:14;13465:5;13473:2;13465:10;13453:1;:8;:22;;13432:107;;;;;;;;;;;;;;;;-1:-1:-1;13718:13:53;13620:2;13718:13;13712:20;;13281:490::o;898:8536:11:-;;;;;;;;;-1:-1:-1;898:8536:11;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;5:118:-1:-;;72:46;110:6;97:20;72:46;;130:122;;208:39;239:6;233:13;208:39;;277:707;;387:4;375:17;;371:27;-1:-1;361:2;;412:1;409;402:12;361:2;449:6;436:20;471:80;486:64;543:6;486:64;;;471:80;;;462:89;;568:5;593:6;586:5;579:21;623:4;615:6;611:17;601:27;;645:4;640:3;636:14;629:21;;698:6;745:3;737:4;729:6;725:17;720:3;716:27;713:36;710:2;;;762:1;759;752:12;710:2;787:1;772:206;797:6;794:1;791:13;772:206;;;855:3;877:37;910:3;898:10;877:37;;;865:50;;-1:-1;938:4;929:14;;;;957;;;;;819:1;812:9;772:206;;;776:14;354:630;;;;;;;;1008:693;;1123:4;1111:17;;1107:27;-1:-1;1097:2;;1148:1;1145;1138:12;1097:2;1185:6;1172:20;1207:85;1222:69;1284:6;1222:69;;1207:85;1320:21;;;1364:4;1352:17;;;;1198:94;;-1:-1;1377:14;;1352:17;1472:1;1457:238;1482:6;1479:1;1476:13;1457:238;;;1565:3;1552:17;1544:6;1540:30;1589:42;1627:3;1615:10;1589:42;;;1577:55;;-1:-1;1655:4;1646:14;;;;1674;;;;;1504:1;1497:9;1457:238;;1745:791;;1889:4;1877:17;;1873:27;-1:-1;1863:2;;1914:1;1911;1904:12;1863:2;1944:6;1938:13;1966:103;1981:87;2061:6;1981:87;;1966:103;1957:112;;2086:5;2111:6;2104:5;2097:21;2141:4;2133:6;2129:17;2119:27;;2163:4;2158:3;2154:14;2147:21;;2216:6;2263:3;2255:4;2247:6;2243:17;2238:3;2234:27;2231:36;2228:2;;;2280:1;2277;2270:12;2228:2;2305:1;2290:240;2315:6;2312:1;2309:13;2290:240;;;2373:3;2395:71;2462:3;2450:10;2395:71;;;2383:84;;-1:-1;2490:4;2481:14;;;;2518:4;2509:14;;;;;2337:1;2330:9;2290:240;;2576:735;;2705:4;2693:17;;2689:27;-1:-1;2679:2;;2730:1;2727;2720:12;2679:2;2767:6;2754:20;2789:99;2804:83;2880:6;2804:83;;2789:99;2916:21;;;2960:4;2948:17;;;;2780:108;;-1:-1;2973:14;;2948:17;3068:1;3053:252;3078:6;3075:1;3072:13;3053:252;;;3161:3;3148:17;3140:6;3136:30;3185:56;3237:3;3225:10;3185:56;;;3173:69;;-1:-1;3265:4;3256:14;;;;3284;;;;;3100:1;3093:9;3053:252;;3319:116;;3394:36;3422:6;3416:13;3394:36;;3442:122;;3520:39;3551:6;3545:13;3520:39;;3572:432;;3662:4;3650:17;;3646:27;-1:-1;3636:2;;3687:1;3684;3677:12;3636:2;3724:6;3711:20;3746:60;3761:44;3798:6;3761:44;;3746:60;3737:69;;3826:6;3819:5;3812:21;3862:4;3854:6;3850:17;3895:4;3888:5;3884:16;3930:3;3921:6;3916:3;3912:16;3909:25;3906:2;;;3947:1;3944;3937:12;3906:2;3957:41;3991:6;3986:3;3981;3957:41;;4493:685;;4616:4;4604:9;4599:3;4595:19;4591:30;4588:2;;;4634:1;4631;4624:12;4588:2;4652:20;4667:4;4652:20;;;4643:29;-1:-1;4729:1;4760:58;4814:3;4794:9;4760:58;;;4736:83;;-1:-1;4885:2;4918:60;4974:3;4950:22;;;4918:60;;;4911:4;4904:5;4900:16;4893:86;4840:150;5063:2;5096:60;5152:3;5143:6;5132:9;5128:22;5096:60;;;5089:4;5082:5;5078:16;5071:86;5000:168;4582:596;;;;;5941:2205;;6049:5;6037:9;6032:3;6028:19;6024:31;6021:2;;;6068:1;6065;6058:12;6021:2;6086:21;6101:5;6086:21;;;6077:30;-1:-1;6165:1;6196:49;6241:3;6221:9;6196:49;;;6172:74;;-1:-1;6315:2;6348:49;6393:3;6369:22;;;6348:49;;;6341:4;6334:5;6330:16;6323:75;6267:142;6474:2;6507:49;6552:3;6543:6;6532:9;6528:22;6507:49;;;6500:4;6493:5;6489:16;6482:75;6419:149;6627:2;6660:49;6705:3;6696:6;6685:9;6681:22;6660:49;;;6653:4;6646:5;6642:16;6635:75;6578:143;6783:3;6817:49;6862:3;6853:6;6842:9;6838:22;6817:49;;;6810:4;6803:5;6799:16;6792:75;6731:147;6940:3;6974:49;7019:3;7010:6;6999:9;6995:22;6974:49;;;6967:4;6960:5;6956:16;6949:75;6888:147;7089:3;7123:49;7168:3;7159:6;7148:9;7144:22;7123:49;;;7116:4;7109:5;7105:16;7098:75;7045:139;7238:3;7272:49;7317:3;7308:6;7297:9;7293:22;7272:49;;;7265:4;7258:5;7254:16;7247:75;7194:139;7400:3;7435:49;7480:3;7471:6;7460:9;7456:22;7435:49;;;7427:5;7420;7416:17;7409:76;7343:153;7546:3;7581:49;7626:3;7617:6;7606:9;7602:22;7581:49;;;7573:5;7566;7562:17;7555:76;7506:136;7730:3;7719:9;7715:19;7702:33;7755:18;7747:6;7744:30;7741:2;;;7787:1;7784;7777:12;7741:2;7823:54;7873:3;7864:6;7853:9;7849:22;7823:54;;;7815:5;7808;7804:17;7797:81;7652:237;7977:3;7966:9;7962:19;7949:33;8002:18;7994:6;7991:30;7988:2;;;8034:1;8031;8024:12;7988:2;8070:54;8120:3;8111:6;8100:9;8096:22;8070:54;;;8062:5;8055;8051:17;8044:81;7899:237;6015:2131;;;;;10397:118;;10464:46;10502:6;10489:20;10464:46;;10651:118;;10727:37;10756:6;10750:13;10727:37;;10776:263;;10891:2;10879:9;10870:7;10866:23;10862:32;10859:2;;;10907:1;10904;10897:12;10859:2;10942:1;10959:64;11015:7;10995:9;10959:64;;;10949:74;10853:186;-1:-1;;;;10853:186;11046:512;;;11197:2;11185:9;11176:7;11172:23;11168:32;11165:2;;;11213:1;11210;11203:12;11165:2;11248:1;11265:53;11310:7;11290:9;11265:53;;;11255:63;;11227:97;11383:2;11372:9;11368:18;11355:32;11407:18;11399:6;11396:30;11393:2;;;11439:1;11436;11429:12;11393:2;11459:83;11534:7;11525:6;11514:9;11510:22;11459:83;;;11449:93;;11334:214;11159:399;;;;;;11565:470;;;11695:2;11683:9;11674:7;11670:23;11666:32;11663:2;;;11711:1;11708;11701:12;11663:2;11746:1;11763:53;11808:7;11788:9;11763:53;;;11753:63;;11725:97;11881:2;11870:9;11866:18;11853:32;11905:18;11897:6;11894:30;11891:2;;;11937:1;11934;11927:12;11891:2;11957:62;12011:7;12002:6;11991:9;11987:22;11957:62;;12042:366;;;12163:2;12151:9;12142:7;12138:23;12134:32;12131:2;;;12179:1;12176;12169:12;12131:2;12214:1;12231:53;12276:7;12256:9;12231:53;;;12221:63;;12193:97;12321:2;12339:53;12384:7;12375:6;12364:9;12360:22;12339:53;;12415:438;;12578:2;12566:9;12557:7;12553:23;12549:32;12546:2;;;12594:1;12591;12584:12;12546:2;12629:24;;12673:18;12662:30;;12659:2;;;12705:1;12702;12695:12;12659:2;12725:112;12829:7;12820:6;12809:9;12805:22;12725:112;;12860:676;;;13050:2;13038:9;13029:7;13025:23;13021:32;13018:2;;;13066:1;13063;13056:12;13018:2;13101:31;;13152:18;13141:30;;13138:2;;;13184:1;13181;13174:12;13138:2;13204:97;13293:7;13284:6;13273:9;13269:22;13204:97;;;13194:107;;13080:227;13366:2;13355:9;13351:18;13338:32;13390:18;13382:6;13379:30;13376:2;;;13422:1;13419;13412:12;13376:2;13442:78;13512:7;13503:6;13492:9;13488:22;13442:78;;13543:257;;13655:2;13643:9;13634:7;13630:23;13626:32;13623:2;;;13671:1;13668;13661:12;13623:2;13706:1;13723:61;13776:7;13756:9;13723:61;;13807:317;;13949:2;13937:9;13928:7;13924:23;13920:32;13917:2;;;13965:1;13962;13955:12;13917:2;14000:1;14017:91;14100:7;14080:9;14017:91;;14131:498;;;14275:2;14263:9;14254:7;14250:23;14246:32;14243:2;;;14291:1;14288;14281:12;14243:2;14326:31;;14377:18;14366:30;;14363:2;;;14409:1;14406;14399:12;14363:2;14429:76;14497:7;14488:6;14477:9;14473:22;14429:76;;;14419:86;;14305:206;14542:2;14560:53;14605:7;14596:6;14585:9;14581:22;14560:53;;14636:263;;14751:2;14739:9;14730:7;14726:23;14722:32;14719:2;;;14767:1;14764;14757:12;14719:2;14802:1;14819:64;14875:7;14855:9;14819:64;;14906:110;14979:31;15004:5;14979:31;;;14974:3;14967:44;14961:55;;;15090:755;;15271:77;15342:5;15271:77;;;15366:6;15361:3;15354:19;15390:4;15385:3;15381:14;15374:21;;15435:79;15508:5;15435:79;;;15535:1;15520:303;15545:6;15542:1;15539:13;15520:303;;;15585:103;15684:3;15675:6;15669:13;15585:103;;;15705:83;15781:6;15705:83;;;15811:4;15802:14;;;;;15695:93;-1:-1;15567:1;15560:9;15520:303;;;-1:-1;15836:3;;15250:595;-1:-1;;;;15250:595;15912:864;;16085:73;16152:5;16085:73;;;16176:6;16171:3;16164:19;16200:4;16195:3;16191:14;16184:21;;16248:3;16290:4;16282:6;16278:17;16273:3;16269:27;16316:75;16385:5;16316:75;;;16412:1;16397:340;16422:6;16419:1;16416:13;16397:340;;;16484:9;16478:4;16474:20;16469:3;16462:33;16510:96;16601:4;16592:6;16586:13;16510:96;;;16502:104;;16623:79;16695:6;16623:79;;;16725:4;16716:14;;;;;16613:89;-1:-1;16444:1;16437:9;16397:340;;;-1:-1;16750:4;;16064:712;-1:-1;;;;;;16064:712;16865:749;;17044:76;17114:5;17044:76;;;17138:6;17133:3;17126:19;17162:4;17157:3;17153:14;17146:21;;17207:78;17279:5;17207:78;;;17306:1;17291:301;17316:6;17313:1;17310:13;17291:301;;;17356:101;17453:3;17444:6;17438:13;17356:101;;;17474:82;17549:6;17474:82;;;17579:5;17570:15;;;;;17464:92;-1:-1;17338:1;17331:9;17291:301;;17653:590;;17788:54;17836:5;17788:54;;;17860:6;17855:3;17848:19;17884:4;17879:3;17875:14;17868:21;;17929:56;17979:5;17929:56;;;18006:1;17991:230;18016:6;18013:1;18010:13;17991:230;;;18056:53;18105:3;18096:6;18090:13;18056:53;;;18126:60;18179:6;18126:60;;;18209:4;18200:14;;;;;18116:70;-1:-1;18038:1;18031:9;17991:230;;18251:110;18324:31;18349:5;18324:31;;18368:107;18439:30;18463:5;18439:30;;18482:289;;18578:34;18606:5;18578:34;;;18629:6;18624:3;18617:19;18641:63;18697:6;18690:4;18685:3;18681:14;18674:4;18667:5;18663:16;18641:63;;;18736:29;18758:6;18736:29;;;18716:50;;;18729:4;18716:50;;18558:213;-1:-1;;;18558:213;18779:397;18934:2;18922:15;;18971:66;18966:2;18957:12;;18950:88;19072:66;19067:2;19058:12;;19051:88;19167:2;19158:12;;18915:261;19185:296;19340:2;19328:15;;19377:66;19372:2;19363:12;;19356:88;19472:2;19463:12;;19321:160;19490:397;19645:2;19633:15;;19682:66;19677:2;19668:12;;19661:88;19783:66;19778:2;19769:12;;19762:88;19878:2;19869:12;;19626:261;19896:397;20051:2;20039:15;;20088:66;20083:2;20074:12;;20067:88;20189:66;20184:2;20175:12;;20168:88;20284:2;20275:12;;20032:261;20362:695;20575:22;;20499:4;20490:14;;;20609:57;20494:3;20575:22;20609:57;;;20519:159;20759:4;20752:5;20748:16;20742:23;20777:62;20833:4;20828:3;20824:14;20811:11;20777:62;;;20688:163;20950:4;20943:5;20939:16;20933:23;20968:62;21024:4;21019:3;21015:14;21002:11;20968:62;;;20861:181;20472:585;;;;21876:2419;22091:22;;21876:2419;;22013:5;22004:15;;;22125:61;22008:3;22091:22;22125:61;;;22034:164;22282:4;22275:5;22271:16;22265:23;22300:62;22356:4;22351:3;22347:14;22334:11;22300:62;;;22208:166;22465:4;22458:5;22454:16;22448:23;22483:62;22539:4;22534:3;22530:14;22517:11;22483:62;;;22384:173;22642:4;22635:5;22631:16;22625:23;22660:62;22716:4;22711:3;22707:14;22694:11;22660:62;;;22567:167;22822:4;22815:5;22811:16;22805:23;22840:62;22896:4;22891:3;22887:14;22874:11;22840:62;;;22744:170;23002:4;22995:5;22991:16;22985:23;23020:62;23076:4;23071:3;23067:14;23054:11;23020:62;;;22924:170;23174:4;23167:5;23163:16;23157:23;23192:62;23248:4;23243:3;23239:14;23226:11;23192:62;;;23104:162;23346:4;23339:5;23335:16;23329:23;23364:62;23420:4;23415:3;23411:14;23398:11;23364:62;;;23276:162;23531:5;23524;23520:17;23514:24;23550:63;23606:5;23601:3;23597:15;23584:11;23550:63;;;23448:177;23701:5;23694;23690:17;23684:24;23720:63;23776:5;23771:3;23767:15;23754:11;23720:63;;;23635:160;23881:5;23874;23870:17;23864:24;23934:3;23928:4;23924:14;23916:5;23911:3;23907:15;23900:39;23954:66;24015:4;24002:11;23954:66;;;23946:74;;23805:227;24118:5;24111;24107:17;24101:24;24171:3;24165:4;24161:14;24153:5;24148:3;24144:15;24137:39;24191:66;24252:4;24239:11;24191:66;;;24183:74;21986:2309;-1:-1;;;;;21986:2309;26852:1587;27065:22;;26987:5;26978:15;;;27099:61;26982:3;27065:22;27099:61;;;27008:164;27258:4;27251:5;27247:16;27241:23;27276:62;27332:4;27327:3;27323:14;27310:11;27276:62;;;27182:168;27434:4;27427:5;27423:16;27417:23;27452:62;27508:4;27503:3;27499:14;27486:11;27452:62;;;27360:166;27612:4;27605:5;27601:16;27595:23;27630:62;27686:4;27681:3;27677:14;27664:11;27630:62;;;27536:168;27791:4;27784:5;27780:16;27774:23;27809:62;27865:4;27860:3;27856:14;27843:11;27809:62;;;27714:169;27972:4;27965:5;27961:16;27955:23;27990:62;28046:4;28041:3;28037:14;28024:11;27990:62;;;27893:171;28151:4;28144:5;28140:16;28134:23;28169:62;28225:4;28220:3;28216:14;28203:11;28169:62;;;28074:169;28332:4;28325:5;28321:16;28315:23;28350:62;28406:4;28401:3;28397:14;28384:11;28350:62;;30228:104;30297:29;30320:5;30297:29;;30339:193;30447:2;30432:18;;30461:61;30436:9;30495:6;30461:61;;30539:294;30675:2;30660:18;;30689:61;30664:9;30723:6;30689:61;;;30761:62;30819:2;30808:9;30804:18;30795:6;30761:62;;30840:770;31166:2;31180:47;;;31151:18;;31241:144;31151:18;31371:6;31241:144;;;31233:152;;31433:9;31427:4;31423:20;31418:2;31407:9;31403:18;31396:48;31458:142;31595:4;31586:6;31458:142;;31617:417;31813:2;31827:47;;;31798:18;;31888:136;31798:18;32010:6;31888:136;;32041:429;32243:2;32257:47;;;32228:18;;32318:142;32228:18;32446:6;32318:142;;32477:590;32713:2;32727:47;;;32698:18;;32788:98;32698:18;32872:6;32788:98;;;32780:106;;32934:9;32928:4;32924:20;32919:2;32908:9;32904:18;32897:48;32959:98;33052:4;33043:6;32959:98;;33074:189;33180:2;33165:18;;33194:59;33169:9;33226:6;33194:59;;33270:387;33451:2;33465:47;;;33436:18;;33526:121;33436:18;33526:121;;33664:387;33845:2;33859:47;;;33830:18;;33920:121;33830:18;33920:121;;34058:387;34239:2;34253:47;;;34224:18;;34314:121;34224:18;34314:121;;34452:387;34633:2;34647:47;;;34618:18;;34708:121;34618:18;34708:121;;34846:507;35088:3;35073:19;;35103:115;35077:9;35191:6;35103:115;;;35229:114;35339:2;35328:9;35324:18;35315:6;35229:114;;35360:333;35514:2;35528:47;;;35499:18;;35589:94;35499:18;35669:6;35589:94;;35700:298;35860:3;35845:19;;35875:113;35849:9;35961:6;35875:113;;36005:193;36113:2;36098:18;;36127:61;36102:9;36161:6;36127:61;;36205:294;36341:2;36326:18;;36355:61;36330:9;36389:6;36355:61;;;36427:62;36485:2;36474:9;36470:18;36461:6;36427:62;;36506:256;36568:2;36562:9;36594:17;;;36669:18;36654:34;;36690:22;;;36651:62;36648:2;;;36726:1;36723;36716:12;36648:2;36742;36735:22;36546:216;;-1:-1;36546:216;36769:258;;36928:18;36920:6;36917:30;36914:2;;;36960:1;36957;36950:12;36914:2;-1:-1;36989:4;36977:17;;;37007:15;;36851:176;37876:254;;38015:18;38007:6;38004:30;38001:2;;;38047:1;38044;38037:12;38001:2;-1:-1;38120:4;38091;38068:17;;;;38087:9;38064:33;38110:15;;37938:192;38404:144;38536:4;38524:17;;38505:43;38994:130;39107:12;;39091:33;40196:128;40276:42;40265:54;;40248:76;40331:79;40400:5;40383:27;40417:151;40496:66;40485:78;;40468:100;40661:88;40739:4;40728:16;;40711:38;40891:92;40964:13;40957:21;;40940:43;41258:145;41339:6;41334:3;41329;41316:30;-1:-1;41395:1;41377:16;;41370:27;41309:94;41412:268;41477:1;41484:101;41498:6;41495:1;41492:13;41484:101;;;41565:11;;;41559:18;41546:11;;;41539:39;41520:2;41513:10;41484:101;;;41600:6;41597:1;41594:13;41591:2;;;-1:-1;;41665:1;41647:16;;41640:27;41461:219;41688:97;41776:2;41756:14;41772:7;41752:28;;41736:49" } } }, @@ -585,149 +612,171 @@ "2.0.0/extensions/OrderValidator/OrderValidator.sol": { "id": 11 }, - "2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol": { + "2.0.0/protocol/AssetProxy/ERC20Proxy.sol": { "id": 12 }, - "2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol": { + "2.0.0/protocol/AssetProxy/ERC721Proxy.sol": { "id": 13 }, - "2.0.0/protocol/Exchange/Exchange.sol": { + "2.0.0/protocol/AssetProxy/MixinAuthorizable.sol": { "id": 14 }, - "2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol": { + "2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol": { "id": 15 }, - "2.0.0/protocol/Exchange/MixinExchangeCore.sol": { + "2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol": { "id": 16 }, - "2.0.0/protocol/Exchange/MixinMatchOrders.sol": { + "2.0.0/protocol/AssetProxy/mixins/MAuthorizable.sol": { "id": 17 }, - "2.0.0/protocol/Exchange/MixinSignatureValidator.sol": { + "2.0.0/protocol/Exchange/Exchange.sol": { "id": 18 }, - "2.0.0/protocol/Exchange/MixinTransactions.sol": { + "2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol": { "id": 19 }, - "2.0.0/protocol/Exchange/MixinWrapperFunctions.sol": { + "2.0.0/protocol/Exchange/MixinExchangeCore.sol": { "id": 20 }, - "2.0.0/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol": { + "2.0.0/protocol/Exchange/MixinMatchOrders.sol": { "id": 21 }, - "2.0.0/protocol/Exchange/interfaces/IExchange.sol": { + "2.0.0/protocol/Exchange/MixinSignatureValidator.sol": { "id": 22 }, - "2.0.0/protocol/Exchange/interfaces/IExchangeCore.sol": { + "2.0.0/protocol/Exchange/MixinTransactions.sol": { "id": 23 }, - "2.0.0/protocol/Exchange/interfaces/IMatchOrders.sol": { + "2.0.0/protocol/Exchange/MixinWrapperFunctions.sol": { "id": 24 }, - "2.0.0/protocol/Exchange/interfaces/ISignatureValidator.sol": { + "2.0.0/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol": { "id": 25 }, - "2.0.0/protocol/Exchange/interfaces/ITransactions.sol": { + "2.0.0/protocol/Exchange/interfaces/IExchange.sol": { "id": 26 }, - "2.0.0/protocol/Exchange/interfaces/IValidator.sol": { + "2.0.0/protocol/Exchange/interfaces/IExchangeCore.sol": { "id": 27 }, - "2.0.0/protocol/Exchange/interfaces/IWallet.sol": { + "2.0.0/protocol/Exchange/interfaces/IMatchOrders.sol": { "id": 28 }, - "2.0.0/protocol/Exchange/interfaces/IWrapperFunctions.sol": { + "2.0.0/protocol/Exchange/interfaces/ISignatureValidator.sol": { "id": 29 }, - "2.0.0/protocol/Exchange/libs/LibAbiEncoder.sol": { + "2.0.0/protocol/Exchange/interfaces/ITransactions.sol": { "id": 30 }, - "2.0.0/protocol/Exchange/libs/LibConstants.sol": { + "2.0.0/protocol/Exchange/interfaces/IValidator.sol": { "id": 31 }, - "2.0.0/protocol/Exchange/libs/LibEIP712.sol": { + "2.0.0/protocol/Exchange/interfaces/IWallet.sol": { "id": 32 }, - "2.0.0/protocol/Exchange/libs/LibExchangeErrors.sol": { + "2.0.0/protocol/Exchange/interfaces/IWrapperFunctions.sol": { "id": 33 }, - "2.0.0/protocol/Exchange/libs/LibFillResults.sol": { + "2.0.0/protocol/Exchange/libs/LibAbiEncoder.sol": { "id": 34 }, - "2.0.0/protocol/Exchange/libs/LibMath.sol": { + "2.0.0/protocol/Exchange/libs/LibConstants.sol": { "id": 35 }, - "2.0.0/protocol/Exchange/libs/LibOrder.sol": { + "2.0.0/protocol/Exchange/libs/LibEIP712.sol": { "id": 36 }, - "2.0.0/protocol/Exchange/mixins/MAssetProxyDispatcher.sol": { + "2.0.0/protocol/Exchange/libs/LibExchangeErrors.sol": { "id": 37 }, - "2.0.0/protocol/Exchange/mixins/MExchangeCore.sol": { + "2.0.0/protocol/Exchange/libs/LibFillResults.sol": { "id": 38 }, - "2.0.0/protocol/Exchange/mixins/MMatchOrders.sol": { + "2.0.0/protocol/Exchange/libs/LibMath.sol": { "id": 39 }, - "2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol": { + "2.0.0/protocol/Exchange/libs/LibOrder.sol": { "id": 40 }, - "2.0.0/protocol/Exchange/mixins/MTransactions.sol": { + "2.0.0/protocol/Exchange/mixins/MAssetProxyDispatcher.sol": { "id": 41 }, - "2.0.0/tokens/ERC20Token/ERC20Token.sol": { + "2.0.0/protocol/Exchange/mixins/MExchangeCore.sol": { "id": 42 }, - "2.0.0/tokens/ERC20Token/IERC20Token.sol": { + "2.0.0/protocol/Exchange/mixins/MMatchOrders.sol": { "id": 43 }, - "2.0.0/tokens/ERC721Token/ERC721Token.sol": { + "2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol": { "id": 44 }, - "2.0.0/tokens/ERC721Token/IERC721Receiver.sol": { + "2.0.0/protocol/Exchange/mixins/MTransactions.sol": { "id": 45 }, - "2.0.0/tokens/ERC721Token/IERC721Token.sol": { + "2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol": { "id": 46 }, - "2.0.0/tokens/EtherToken/IEtherToken.sol": { + "2.0.0/tokens/ERC20Token/ERC20Token.sol": { "id": 47 }, - "2.0.0/utils/LibBytes/LibBytes.sol": { + "2.0.0/tokens/ERC20Token/IERC20Token.sol": { "id": 48 }, - "2.0.0/utils/Ownable/IOwnable.sol": { + "2.0.0/tokens/ERC721Token/ERC721Token.sol": { "id": 49 }, - "2.0.0/utils/Ownable/Ownable.sol": { + "2.0.0/tokens/ERC721Token/IERC721Receiver.sol": { "id": 50 }, - "2.0.0/utils/SafeMath/SafeMath.sol": { + "2.0.0/tokens/ERC721Token/IERC721Token.sol": { "id": 51 + }, + "2.0.0/tokens/EtherToken/IEtherToken.sol": { + "id": 52 + }, + "2.0.0/utils/LibBytes/LibBytes.sol": { + "id": 53 + }, + "2.0.0/utils/Ownable/IOwnable.sol": { + "id": 54 + }, + "2.0.0/utils/Ownable/Ownable.sol": { + "id": 55 + }, + "2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol": { + "id": 56 + }, + "2.0.0/utils/SafeMath/SafeMath.sol": { + "id": 57 } }, "sourceCodes": { - "2.0.0/extensions/Forwarder/Forwarder.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./MixinWeth.sol\";\nimport \"./MixinForwarderCore.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./MixinAssets.sol\";\nimport \"./MixinExchangeWrapper.sol\";\n\n\n// solhint-disable no-empty-blocks\ncontract Forwarder is\n LibConstants,\n MixinWeth,\n MixinAssets,\n MixinExchangeWrapper,\n MixinForwarderCore\n{\n\n constructor (\n address _exchange,\n address _etherToken,\n address _zrxToken,\n bytes memory _zrxAssetData,\n bytes memory _wethAssetData\n )\n public\n LibConstants(\n _exchange,\n _etherToken,\n _zrxToken,\n _zrxAssetData,\n _wethAssetData\n )\n MixinForwarderCore()\n {}\n}\n", + "2.0.0/extensions/Forwarder/Forwarder.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./MixinWeth.sol\";\nimport \"./MixinForwarderCore.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./MixinAssets.sol\";\nimport \"./MixinExchangeWrapper.sol\";\n\n\n// solhint-disable no-empty-blocks\ncontract Forwarder is\n LibConstants,\n MixinWeth,\n MixinAssets,\n MixinExchangeWrapper,\n MixinForwarderCore\n{\n\n constructor (\n address _exchange,\n bytes memory _zrxAssetData,\n bytes memory _wethAssetData\n )\n public\n LibConstants(\n _exchange,\n _zrxAssetData,\n _wethAssetData\n )\n MixinForwarderCore()\n {}\n}\n", "2.0.0/extensions/Forwarder/MixinAssets.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"../../utils/Ownable/Ownable.sol\";\nimport \"../../tokens/ERC20Token/IERC20Token.sol\";\nimport \"../../tokens/ERC721Token/IERC721Token.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MAssets.sol\";\n\n\ncontract MixinAssets is\n Ownable,\n LibConstants,\n MAssets\n{\n\n using LibBytes for bytes;\n\n bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256(\"transfer(address,uint256)\"));\n\n /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to \n /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be\n /// used to withdraw assets that were accidentally sent to this contract.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of ERC20 token to withdraw.\n function withdrawAsset(\n bytes assetData,\n uint256 amount\n )\n external\n onlyOwner\n {\n transferAssetToSender(assetData, amount);\n }\n\n /// @dev Transfers given amount of asset to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferAssetToSender(\n bytes memory assetData,\n uint256 amount\n )\n internal\n {\n bytes4 proxyId = assetData.readBytes4(0);\n\n if (proxyId == ERC20_DATA_ID) {\n transferERC20Token(assetData, amount);\n } else if (proxyId == ERC721_DATA_ID) {\n transferERC721Token(assetData, amount);\n } else {\n revert(\"UNSUPPORTED_ASSET_PROXY\");\n }\n }\n\n /// @dev Decodes ERC20 assetData and transfers given amount to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferERC20Token(\n bytes memory assetData,\n uint256 amount\n )\n internal\n {\n address token = assetData.readAddress(16);\n\n // Transfer tokens.\n // We do a raw call so we can check the success separate\n // from the return data.\n bool success = token.call(abi.encodeWithSelector(\n ERC20_TRANSFER_SELECTOR,\n msg.sender,\n amount\n ));\n require(\n success,\n \"TRANSFER_FAILED\"\n );\n \n // Check return data.\n // If there is no return data, we assume the token incorrectly\n // does not return a bool. In this case we expect it to revert\n // on failure, which was handled above.\n // If the token does return data, we require that it is a single\n // value that evaluates to true.\n assembly {\n if returndatasize {\n success := 0\n if eq(returndatasize, 32) {\n // First 64 bytes of memory are reserved scratch space\n returndatacopy(0, 0, 32)\n success := mload(0)\n }\n }\n }\n require(\n success,\n \"TRANSFER_FAILED\"\n );\n }\n\n /// @dev Decodes ERC721 assetData and transfers given amount to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferERC721Token(\n bytes memory assetData,\n uint256 amount\n )\n internal\n {\n require(\n amount == 1,\n \"INVALID_AMOUNT\"\n );\n // Decode asset data.\n address token = assetData.readAddress(16);\n uint256 tokenId = assetData.readUint256(36);\n\n // Perform transfer.\n IERC721Token(token).transferFrom(\n address(this),\n msg.sender,\n tokenId\n );\n }\n}\n", - "2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MExchangeWrapper.sol\";\nimport \"../../protocol/Exchange/libs/LibAbiEncoder.sol\";\nimport \"../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../protocol/Exchange/libs/LibFillResults.sol\";\nimport \"../../protocol/Exchange/libs/LibMath.sol\";\n\n\ncontract MixinExchangeWrapper is\n LibAbiEncoder,\n LibFillResults,\n LibMath,\n LibConstants,\n MExchangeWrapper\n{\n\n /// @dev Fills the input order.\n /// Returns false if the transaction would otherwise revert.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderNoThrow(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (FillResults memory fillResults)\n {\n // ABI encode calldata for `fillOrder`\n bytes memory fillOrderCalldata = abiEncodeFillOrder(\n order,\n takerAssetFillAmount,\n signature\n );\n\n address exchange = address(EXCHANGE);\n\n // Call `fillOrder` and handle any exceptions gracefully\n assembly {\n let success := call(\n gas, // forward all gas, TODO: look into gas consumption of assert/throw\n exchange, // call address of Exchange contract\n 0, // transfer 0 wei\n add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes)\n mload(fillOrderCalldata), // length of input\n fillOrderCalldata, // write output over input\n 128 // output size is 128 bytes\n )\n switch success\n case 0 {\n mstore(fillResults, 0)\n mstore(add(fillResults, 32), 0)\n mstore(add(fillResults, 64), 0)\n mstore(add(fillResults, 96), 0)\n }\n case 1 {\n mstore(fillResults, mload(fillOrderCalldata))\n mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))\n mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))\n mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96)))\n }\n }\n return fillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH has been sold by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param wethSellAmount Desired amount of WETH to sell.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellWeth(\n LibOrder.Order[] memory orders,\n uint256 wethSellAmount,\n bytes[] memory signatures\n )\n internal\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n bytes memory wethAssetData = WETH_ASSET_DATA;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // We assume that asset being sold by taker is WETH for each order.\n orders[i].makerAssetData = makerAssetData;\n orders[i].takerAssetData = wethAssetData;\n\n // Calculate the remaining amount of WETH to sell\n uint256 remainingTakerAssetFillAmount = safeSub(wethSellAmount, totalFillResults.takerAssetFilledAmount);\n\n // Attempt to sell the remaining amount of WETH\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of takerAsset has been sold\n if (totalFillResults.takerAssetFilledAmount >= wethSellAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.\n /// Returns false if the transaction would otherwise revert.\n /// The asset being sold by taker must always be WETH.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyExactAmountWithWeth(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n internal\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n bytes memory wethAssetData = WETH_ASSET_DATA;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // We assume that asset being sold by taker is WETH for each order.\n orders[i].makerAssetData = makerAssetData;\n orders[i].takerAssetData = wethAssetData;\n\n // Calculate the remaining amount of makerAsset to buy\n uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);\n\n // Convert the remaining amount of makerAsset to buy into remaining amount\n // of takerAsset to sell, assuming entire amount can be sold in the current order\n uint256 remainingTakerAssetFillAmount = getPartialAmount(\n orders[i].takerAssetAmount,\n orders[i].makerAssetAmount,\n remainingMakerAssetFillAmount\n );\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of makerAsset has been bought\n uint256 makerAssetFilledAmount = totalFillResults.makerAssetFilledAmount;\n if (makerAssetFilledAmount >= makerAssetFillAmount) {\n break;\n }\n }\n\n require(\n makerAssetFilledAmount >= makerAssetFillAmount,\n \"COMPLETE_FILL_FAILED\"\n );\n return totalFillResults;\n }\n\n /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account ZRX fees for each order. This will guarantee\n /// that at least zrxBuyAmount of ZRX is purchased (sometimes slightly over due to rounding issues).\n /// It is possible that a request to buy 200 ZRX will require purchasing 202 ZRX\n /// as 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases.\n /// The asset being sold by taker must always be WETH. \n /// @param orders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset.\n /// @param zrxBuyAmount Desired amount of ZRX to buy.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return totalFillResults Amounts filled and fees paid by maker and taker.\n function marketBuyExactZrxWithWeth(\n LibOrder.Order[] memory orders,\n uint256 zrxBuyAmount,\n bytes[] memory signatures\n )\n internal\n returns (FillResults memory totalFillResults)\n {\n // Do nothing if zrxBuyAmount == 0\n if (zrxBuyAmount == 0) {\n return totalFillResults;\n }\n\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n bytes memory wethAssetData = WETH_ASSET_DATA;\n uint256 zrxPurchased = 0;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // All of these are ZRX/WETH, so we can drop the respective assetData from calldata.\n orders[i].makerAssetData = zrxAssetData;\n orders[i].takerAssetData = wethAssetData;\n\n // Calculate the remaining amount of ZRX to buy.\n uint256 remainingZrxBuyAmount = safeSub(zrxBuyAmount, zrxPurchased);\n\n // Convert the remaining amount of ZRX to buy into remaining amount\n // of WETH to sell, assuming entire amount can be sold in the current order.\n uint256 remainingWethSellAmount = getPartialAmount(\n orders[i].takerAssetAmount,\n safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees \n remainingZrxBuyAmount\n );\n\n // Attempt to sell the remaining amount of WETH.\n FillResults memory singleFillResult = fillOrderNoThrow(\n orders[i],\n safeAdd(remainingWethSellAmount, 1), // we add 1 wei to the fill amount to make up for rounding errors\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker.\n addFillResults(totalFillResults, singleFillResult);\n zrxPurchased = safeSub(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid);\n\n // Stop execution if the entire amount of ZRX has been bought.\n if (zrxPurchased >= zrxBuyAmount) {\n break;\n }\n }\n\n require(\n zrxPurchased >= zrxBuyAmount,\n \"COMPLETE_FILL_FAILED\"\n );\n return totalFillResults;\n }\n}\n", - "2.0.0/extensions/Forwarder/MixinForwarderCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MWeth.sol\";\nimport \"./mixins/MAssets.sol\";\nimport \"./mixins/MExchangeWrapper.sol\";\nimport \"./interfaces/IForwarderCore.sol\";\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../protocol/Exchange/libs/LibFillResults.sol\";\nimport \"../../protocol/Exchange/libs/LibMath.sol\";\n\n\ncontract MixinForwarderCore is\n LibFillResults,\n LibMath,\n LibConstants,\n MWeth,\n MAssets,\n MExchangeWrapper,\n IForwarderCore\n{\n\n using LibBytes for bytes;\n\n /// @dev Constructor approves ERC20 proxy to transfer ZRX and WETH on this contract's behalf.\n constructor ()\n public\n {\n address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID);\n if (proxyAddress != address(0)) {\n ETHER_TOKEN.approve(proxyAddress, MAX_UINT);\n ZRX_TOKEN.approve(proxyAddress, MAX_UINT);\n }\n }\n\n /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.\n /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.\n /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).\n /// Any ETH not spent will be refunded to sender.\n /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. \n /// @param signatures Proofs that orders have been created by makers.\n /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.\n /// @param feeSignatures Proofs that feeOrders have been created by makers.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n /// @return Amounts filled and fees paid by maker and taker for both sets of orders.\n function marketSellOrdersWithEth(\n LibOrder.Order[] memory orders,\n bytes[] memory signatures,\n LibOrder.Order[] memory feeOrders,\n bytes[] memory feeSignatures,\n uint256 feePercentage,\n address feeRecipient\n )\n public\n payable\n returns (\n FillResults memory orderFillResults,\n FillResults memory feeOrderFillResults\n )\n {\n // Convert ETH to WETH.\n convertEthToWeth();\n\n uint256 wethSellAmount;\n uint256 zrxBuyAmount;\n uint256 makerAssetAmountPurchased;\n if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {\n // Calculate amount of WETH that won't be spent on ETH fees.\n wethSellAmount = getPartialAmount(\n PERCENTAGE_DENOMINATOR,\n safeAdd(PERCENTAGE_DENOMINATOR, feePercentage),\n msg.value\n );\n // Market sell available WETH.\n // ZRX fees are paid with this contract's balance.\n orderFillResults = marketSellWeth(\n orders,\n wethSellAmount,\n signatures\n );\n // The fee amount must be deducted from the amount transfered back to sender.\n makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);\n } else {\n // 5% of WETH is reserved for filling feeOrders and paying feeRecipient.\n wethSellAmount = getPartialAmount(\n MAX_WETH_FILL_PERCENTAGE,\n PERCENTAGE_DENOMINATOR,\n msg.value\n );\n // Market sell 95% of WETH.\n // ZRX fees are payed with this contract's balance.\n orderFillResults = marketSellWeth(\n orders,\n wethSellAmount,\n signatures\n );\n // Buy back all ZRX spent on fees.\n zrxBuyAmount = orderFillResults.takerFeePaid;\n feeOrderFillResults = marketBuyExactZrxWithWeth(\n feeOrders,\n zrxBuyAmount,\n feeSignatures\n );\n makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;\n }\n\n // Transfer feePercentage of total ETH spent on primary orders to feeRecipient.\n // Refund remaining ETH to msg.sender.\n transferEthFeeAndRefund(\n orderFillResults.takerAssetFilledAmount,\n feeOrderFillResults.takerAssetFilledAmount,\n feePercentage,\n feeRecipient\n );\n\n // Transfer purchased assets to msg.sender.\n transferAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);\n }\n\n /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction.\n /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.\n /// Any ETH not spent will be refunded to sender.\n /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. \n /// @param makerAssetFillAmount Desired amount of makerAsset to purchase.\n /// @param signatures Proofs that orders have been created by makers.\n /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.\n /// @param feeSignatures Proofs that feeOrders have been created by makers.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n /// @return Amounts filled and fees paid by maker and taker for both sets of orders.\n function marketBuyOrdersWithEth(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures,\n LibOrder.Order[] memory feeOrders,\n bytes[] memory feeSignatures,\n uint256 feePercentage,\n address feeRecipient\n )\n public\n payable\n returns (\n FillResults memory orderFillResults,\n FillResults memory feeOrderFillResults\n )\n {\n // Convert ETH to WETH.\n convertEthToWeth();\n\n uint256 zrxBuyAmount;\n uint256 makerAssetAmountPurchased;\n if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {\n // If the makerAsset is ZRX, it is not necessary to pay fees out of this\n // contracts's ZRX balance because fees are factored into the price of the order.\n orderFillResults = marketBuyExactZrxWithWeth(\n orders,\n makerAssetFillAmount,\n signatures\n );\n // The fee amount must be deducted from the amount transfered back to sender.\n makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);\n } else {\n // Attemp to purchase desired amount of makerAsset.\n // ZRX fees are payed with this contract's balance.\n orderFillResults = marketBuyExactAmountWithWeth(\n orders,\n makerAssetFillAmount,\n signatures\n );\n // Buy back all ZRX spent on fees.\n zrxBuyAmount = orderFillResults.takerFeePaid;\n feeOrderFillResults = marketBuyExactZrxWithWeth(\n feeOrders,\n zrxBuyAmount,\n feeSignatures\n );\n makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;\n }\n\n // Transfer feePercentage of total ETH spent on primary orders to feeRecipient.\n // Refund remaining ETH to msg.sender.\n transferEthFeeAndRefund(\n orderFillResults.takerAssetFilledAmount,\n feeOrderFillResults.takerAssetFilledAmount,\n feePercentage,\n feeRecipient\n );\n\n // Transfer purchased assets to msg.sender.\n transferAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);\n }\n}\n", - "2.0.0/extensions/Forwarder/MixinWeth.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../protocol/Exchange/libs/LibMath.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MWeth.sol\";\n\n\ncontract MixinWeth is\n LibMath,\n LibConstants,\n MWeth\n{\n\n /// @dev Default payabale function, this allows us to withdraw WETH\n function ()\n public\n payable\n {\n require(\n msg.sender == address(ETHER_TOKEN),\n \"DEFAULT_FUNCTION_WETH_CONTRACT_ONLY\"\n );\n }\n\n /// @dev Converts message call's ETH value into WETH.\n function convertEthToWeth()\n internal\n {\n require(\n msg.value > 0,\n \"INVALID_MSG_VALUE\"\n );\n ETHER_TOKEN.deposit.value(msg.value)();\n }\n\n /// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.\n /// Refunds any excess ETH to msg.sender.\n /// @param wethSoldExcludingFeeOrders Amount of WETH sold when filling primary orders.\n /// @param wethSoldForZrx Amount of WETH sold when purchasing ZRX required for primary order fees.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n function transferEthFeeAndRefund(\n uint256 wethSoldExcludingFeeOrders,\n uint256 wethSoldForZrx,\n uint256 feePercentage,\n address feeRecipient\n )\n internal\n {\n // Ensure feePercentage is less than 5%.\n require(\n feePercentage <= MAX_FEE_PERCENTAGE,\n \"FEE_PERCENTAGE_TOO_LARGE\"\n );\n\n // Ensure that no extra WETH owned by this contract has been sold.\n uint256 wethSold = safeAdd(wethSoldExcludingFeeOrders, wethSoldForZrx);\n require(\n wethSold <= msg.value,\n \"OVERSOLD_WETH\"\n );\n\n // Calculate amount of WETH that hasn't been sold.\n uint256 wethRemaining = safeSub(msg.value, wethSold);\n\n // Calculate ETH fee to pay to feeRecipient.\n uint256 ethFee = getPartialAmount(\n feePercentage,\n PERCENTAGE_DENOMINATOR,\n wethSoldExcludingFeeOrders\n );\n\n // Ensure fee is less than amount of WETH remaining.\n require(\n ethFee <= wethRemaining,\n \"INSUFFICIENT_ETH_REMAINING\"\n );\n \n // Do nothing if no WETH remaining\n if (wethRemaining > 0) {\n // Convert remaining WETH to ETH\n ETHER_TOKEN.withdraw(wethRemaining);\n\n // Pay ETH to feeRecipient\n if (ethFee > 0) {\n feeRecipient.transfer(ethFee);\n }\n\n // Refund remaining ETH to msg.sender.\n uint256 ethRefund = safeSub(wethRemaining, ethFee);\n if (ethRefund > 0) {\n msg.sender.transfer(ethRefund);\n }\n }\n }\n}\n", + "2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MExchangeWrapper.sol\";\nimport \"../../protocol/Exchange/libs/LibAbiEncoder.sol\";\nimport \"../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../protocol/Exchange/libs/LibFillResults.sol\";\nimport \"../../protocol/Exchange/libs/LibMath.sol\";\n\n\ncontract MixinExchangeWrapper is\n LibAbiEncoder,\n LibFillResults,\n LibMath,\n LibConstants,\n MExchangeWrapper\n{\n\n /// @dev Fills the input order.\n /// Returns false if the transaction would otherwise revert.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderNoThrow(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (FillResults memory fillResults)\n {\n // ABI encode calldata for `fillOrder`\n bytes memory fillOrderCalldata = abiEncodeFillOrder(\n order,\n takerAssetFillAmount,\n signature\n );\n\n address exchange = address(EXCHANGE);\n\n // Call `fillOrder` and handle any exceptions gracefully\n assembly {\n let success := call(\n gas, // forward all gas, TODO: look into gas consumption of assert/throw\n exchange, // call address of Exchange contract\n 0, // transfer 0 wei\n add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes)\n mload(fillOrderCalldata), // length of input\n fillOrderCalldata, // write output over input\n 128 // output size is 128 bytes\n )\n switch success\n case 0 {\n mstore(fillResults, 0)\n mstore(add(fillResults, 32), 0)\n mstore(add(fillResults, 64), 0)\n mstore(add(fillResults, 96), 0)\n }\n case 1 {\n mstore(fillResults, mload(fillOrderCalldata))\n mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))\n mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))\n mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96)))\n }\n }\n return fillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH has been sold by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param wethSellAmount Desired amount of WETH to sell.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellWeth(\n LibOrder.Order[] memory orders,\n uint256 wethSellAmount,\n bytes[] memory signatures\n )\n internal\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n bytes memory wethAssetData = WETH_ASSET_DATA;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // We assume that asset being sold by taker is WETH for each order.\n orders[i].makerAssetData = makerAssetData;\n orders[i].takerAssetData = wethAssetData;\n\n // Calculate the remaining amount of WETH to sell\n uint256 remainingTakerAssetFillAmount = safeSub(wethSellAmount, totalFillResults.takerAssetFilledAmount);\n\n // Attempt to sell the remaining amount of WETH\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of takerAsset has been sold\n if (totalFillResults.takerAssetFilledAmount >= wethSellAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.\n /// Returns false if the transaction would otherwise revert.\n /// The asset being sold by taker must always be WETH.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyExactAmountWithWeth(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n internal\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n bytes memory wethAssetData = WETH_ASSET_DATA;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // We assume that asset being sold by taker is WETH for each order.\n orders[i].makerAssetData = makerAssetData;\n orders[i].takerAssetData = wethAssetData;\n\n // Calculate the remaining amount of makerAsset to buy\n uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);\n\n // Convert the remaining amount of makerAsset to buy into remaining amount\n // of takerAsset to sell, assuming entire amount can be sold in the current order\n uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(\n orders[i].takerAssetAmount,\n orders[i].makerAssetAmount,\n remainingMakerAssetFillAmount\n );\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of makerAsset has been bought\n uint256 makerAssetFilledAmount = totalFillResults.makerAssetFilledAmount;\n if (makerAssetFilledAmount >= makerAssetFillAmount) {\n break;\n }\n }\n\n require(\n makerAssetFilledAmount >= makerAssetFillAmount,\n \"COMPLETE_FILL_FAILED\"\n );\n return totalFillResults;\n }\n\n /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account ZRX fees for each order. This will guarantee\n /// that at least zrxBuyAmount of ZRX is purchased (sometimes slightly over due to rounding issues).\n /// It is possible that a request to buy 200 ZRX will require purchasing 202 ZRX\n /// as 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases.\n /// The asset being sold by taker must always be WETH. \n /// @param orders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset.\n /// @param zrxBuyAmount Desired amount of ZRX to buy.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return totalFillResults Amounts filled and fees paid by maker and taker.\n function marketBuyExactZrxWithWeth(\n LibOrder.Order[] memory orders,\n uint256 zrxBuyAmount,\n bytes[] memory signatures\n )\n internal\n returns (FillResults memory totalFillResults)\n {\n // Do nothing if zrxBuyAmount == 0\n if (zrxBuyAmount == 0) {\n return totalFillResults;\n }\n\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n bytes memory wethAssetData = WETH_ASSET_DATA;\n uint256 zrxPurchased = 0;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // All of these are ZRX/WETH, so we can drop the respective assetData from calldata.\n orders[i].makerAssetData = zrxAssetData;\n orders[i].takerAssetData = wethAssetData;\n\n // Calculate the remaining amount of ZRX to buy.\n uint256 remainingZrxBuyAmount = safeSub(zrxBuyAmount, zrxPurchased);\n\n // Convert the remaining amount of ZRX to buy into remaining amount\n // of WETH to sell, assuming entire amount can be sold in the current order.\n uint256 remainingWethSellAmount = getPartialAmountFloor(\n orders[i].takerAssetAmount,\n safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees \n remainingZrxBuyAmount\n );\n\n // Attempt to sell the remaining amount of WETH.\n FillResults memory singleFillResult = fillOrderNoThrow(\n orders[i],\n safeAdd(remainingWethSellAmount, 1), // we add 1 wei to the fill amount to make up for rounding errors\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker.\n addFillResults(totalFillResults, singleFillResult);\n zrxPurchased = safeSub(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid);\n\n // Stop execution if the entire amount of ZRX has been bought.\n if (zrxPurchased >= zrxBuyAmount) {\n break;\n }\n }\n\n require(\n zrxPurchased >= zrxBuyAmount,\n \"COMPLETE_FILL_FAILED\"\n );\n return totalFillResults;\n }\n}\n", + "2.0.0/extensions/Forwarder/MixinForwarderCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MWeth.sol\";\nimport \"./mixins/MAssets.sol\";\nimport \"./mixins/MExchangeWrapper.sol\";\nimport \"./interfaces/IForwarderCore.sol\";\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../protocol/Exchange/libs/LibFillResults.sol\";\nimport \"../../protocol/Exchange/libs/LibMath.sol\";\n\n\ncontract MixinForwarderCore is\n LibFillResults,\n LibMath,\n LibConstants,\n MWeth,\n MAssets,\n MExchangeWrapper,\n IForwarderCore\n{\n\n using LibBytes for bytes;\n\n /// @dev Constructor approves ERC20 proxy to transfer ZRX and WETH on this contract's behalf.\n constructor ()\n public\n {\n address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID);\n if (proxyAddress != address(0)) {\n ETHER_TOKEN.approve(proxyAddress, MAX_UINT);\n ZRX_TOKEN.approve(proxyAddress, MAX_UINT);\n }\n }\n\n /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.\n /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.\n /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).\n /// Any ETH not spent will be refunded to sender.\n /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. \n /// @param signatures Proofs that orders have been created by makers.\n /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.\n /// @param feeSignatures Proofs that feeOrders have been created by makers.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n /// @return Amounts filled and fees paid by maker and taker for both sets of orders.\n function marketSellOrdersWithEth(\n LibOrder.Order[] memory orders,\n bytes[] memory signatures,\n LibOrder.Order[] memory feeOrders,\n bytes[] memory feeSignatures,\n uint256 feePercentage,\n address feeRecipient\n )\n public\n payable\n returns (\n FillResults memory orderFillResults,\n FillResults memory feeOrderFillResults\n )\n {\n // Convert ETH to WETH.\n convertEthToWeth();\n\n uint256 wethSellAmount;\n uint256 zrxBuyAmount;\n uint256 makerAssetAmountPurchased;\n if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {\n // Calculate amount of WETH that won't be spent on ETH fees.\n wethSellAmount = getPartialAmountFloor(\n PERCENTAGE_DENOMINATOR,\n safeAdd(PERCENTAGE_DENOMINATOR, feePercentage),\n msg.value\n );\n // Market sell available WETH.\n // ZRX fees are paid with this contract's balance.\n orderFillResults = marketSellWeth(\n orders,\n wethSellAmount,\n signatures\n );\n // The fee amount must be deducted from the amount transfered back to sender.\n makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);\n } else {\n // 5% of WETH is reserved for filling feeOrders and paying feeRecipient.\n wethSellAmount = getPartialAmountFloor(\n MAX_WETH_FILL_PERCENTAGE,\n PERCENTAGE_DENOMINATOR,\n msg.value\n );\n // Market sell 95% of WETH.\n // ZRX fees are payed with this contract's balance.\n orderFillResults = marketSellWeth(\n orders,\n wethSellAmount,\n signatures\n );\n // Buy back all ZRX spent on fees.\n zrxBuyAmount = orderFillResults.takerFeePaid;\n feeOrderFillResults = marketBuyExactZrxWithWeth(\n feeOrders,\n zrxBuyAmount,\n feeSignatures\n );\n makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;\n }\n\n // Transfer feePercentage of total ETH spent on primary orders to feeRecipient.\n // Refund remaining ETH to msg.sender.\n transferEthFeeAndRefund(\n orderFillResults.takerAssetFilledAmount,\n feeOrderFillResults.takerAssetFilledAmount,\n feePercentage,\n feeRecipient\n );\n\n // Transfer purchased assets to msg.sender.\n transferAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);\n }\n\n /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction.\n /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.\n /// Any ETH not spent will be refunded to sender.\n /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. \n /// @param makerAssetFillAmount Desired amount of makerAsset to purchase.\n /// @param signatures Proofs that orders have been created by makers.\n /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.\n /// @param feeSignatures Proofs that feeOrders have been created by makers.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n /// @return Amounts filled and fees paid by maker and taker for both sets of orders.\n function marketBuyOrdersWithEth(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures,\n LibOrder.Order[] memory feeOrders,\n bytes[] memory feeSignatures,\n uint256 feePercentage,\n address feeRecipient\n )\n public\n payable\n returns (\n FillResults memory orderFillResults,\n FillResults memory feeOrderFillResults\n )\n {\n // Convert ETH to WETH.\n convertEthToWeth();\n\n uint256 zrxBuyAmount;\n uint256 makerAssetAmountPurchased;\n if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {\n // If the makerAsset is ZRX, it is not necessary to pay fees out of this\n // contracts's ZRX balance because fees are factored into the price of the order.\n orderFillResults = marketBuyExactZrxWithWeth(\n orders,\n makerAssetFillAmount,\n signatures\n );\n // The fee amount must be deducted from the amount transfered back to sender.\n makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);\n } else {\n // Attemp to purchase desired amount of makerAsset.\n // ZRX fees are payed with this contract's balance.\n orderFillResults = marketBuyExactAmountWithWeth(\n orders,\n makerAssetFillAmount,\n signatures\n );\n // Buy back all ZRX spent on fees.\n zrxBuyAmount = orderFillResults.takerFeePaid;\n feeOrderFillResults = marketBuyExactZrxWithWeth(\n feeOrders,\n zrxBuyAmount,\n feeSignatures\n );\n makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;\n }\n\n // Transfer feePercentage of total ETH spent on primary orders to feeRecipient.\n // Refund remaining ETH to msg.sender.\n transferEthFeeAndRefund(\n orderFillResults.takerAssetFilledAmount,\n feeOrderFillResults.takerAssetFilledAmount,\n feePercentage,\n feeRecipient\n );\n\n // Transfer purchased assets to msg.sender.\n transferAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);\n }\n}\n", + "2.0.0/extensions/Forwarder/MixinWeth.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../protocol/Exchange/libs/LibMath.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./mixins/MWeth.sol\";\n\n\ncontract MixinWeth is\n LibMath,\n LibConstants,\n MWeth\n{\n\n /// @dev Default payabale function, this allows us to withdraw WETH\n function ()\n public\n payable\n {\n require(\n msg.sender == address(ETHER_TOKEN),\n \"DEFAULT_FUNCTION_WETH_CONTRACT_ONLY\"\n );\n }\n\n /// @dev Converts message call's ETH value into WETH.\n function convertEthToWeth()\n internal\n {\n require(\n msg.value > 0,\n \"INVALID_MSG_VALUE\"\n );\n ETHER_TOKEN.deposit.value(msg.value)();\n }\n\n /// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.\n /// Refunds any excess ETH to msg.sender.\n /// @param wethSoldExcludingFeeOrders Amount of WETH sold when filling primary orders.\n /// @param wethSoldForZrx Amount of WETH sold when purchasing ZRX required for primary order fees.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n function transferEthFeeAndRefund(\n uint256 wethSoldExcludingFeeOrders,\n uint256 wethSoldForZrx,\n uint256 feePercentage,\n address feeRecipient\n )\n internal\n {\n // Ensure feePercentage is less than 5%.\n require(\n feePercentage <= MAX_FEE_PERCENTAGE,\n \"FEE_PERCENTAGE_TOO_LARGE\"\n );\n\n // Ensure that no extra WETH owned by this contract has been sold.\n uint256 wethSold = safeAdd(wethSoldExcludingFeeOrders, wethSoldForZrx);\n require(\n wethSold <= msg.value,\n \"OVERSOLD_WETH\"\n );\n\n // Calculate amount of WETH that hasn't been sold.\n uint256 wethRemaining = safeSub(msg.value, wethSold);\n\n // Calculate ETH fee to pay to feeRecipient.\n uint256 ethFee = getPartialAmountFloor(\n feePercentage,\n PERCENTAGE_DENOMINATOR,\n wethSoldExcludingFeeOrders\n );\n\n // Ensure fee is less than amount of WETH remaining.\n require(\n ethFee <= wethRemaining,\n \"INSUFFICIENT_ETH_REMAINING\"\n );\n \n // Do nothing if no WETH remaining\n if (wethRemaining > 0) {\n // Convert remaining WETH to ETH\n ETHER_TOKEN.withdraw(wethRemaining);\n\n // Pay ETH to feeRecipient\n if (ethFee > 0) {\n feeRecipient.transfer(ethFee);\n }\n\n // Refund remaining ETH to msg.sender.\n uint256 ethRefund = safeSub(wethRemaining, ethFee);\n if (ethRefund > 0) {\n msg.sender.transfer(ethRefund);\n }\n }\n }\n}\n", "2.0.0/extensions/Forwarder/interfaces/IAssets.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IAssets {\n\n /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to \n /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be\n /// used to withdraw assets that were accidentally sent to this contract.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of ERC20 token to withdraw.\n function withdrawAsset(\n bytes assetData,\n uint256 amount\n )\n external;\n}\n", "2.0.0/extensions/Forwarder/interfaces/IForwarderCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../../protocol/Exchange/libs/LibFillResults.sol\";\n\n\ncontract IForwarderCore {\n\n /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.\n /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.\n /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).\n /// Any ETH not spent will be refunded to sender.\n /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. \n /// @param signatures Proofs that orders have been created by makers.\n /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.\n /// @param feeSignatures Proofs that feeOrders have been created by makers.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n /// @return Amounts filled and fees paid by maker and taker for both sets of orders.\n function marketSellOrdersWithEth(\n LibOrder.Order[] memory orders,\n bytes[] memory signatures,\n LibOrder.Order[] memory feeOrders,\n bytes[] memory feeSignatures,\n uint256 feePercentage,\n address feeRecipient\n )\n public\n payable\n returns (\n LibFillResults.FillResults memory orderFillResults,\n LibFillResults.FillResults memory feeOrderFillResults\n );\n\n /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction.\n /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.\n /// Any ETH not spent will be refunded to sender.\n /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. \n /// @param makerAssetFillAmount Desired amount of makerAsset to purchase.\n /// @param signatures Proofs that orders have been created by makers.\n /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.\n /// @param feeSignatures Proofs that feeOrders have been created by makers.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n /// @return Amounts filled and fees paid by maker and taker for both sets of orders.\n function marketBuyOrdersWithEth(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures,\n LibOrder.Order[] memory feeOrders,\n bytes[] memory feeSignatures,\n uint256 feePercentage,\n address feeRecipient\n )\n public\n payable\n returns (\n LibFillResults.FillResults memory orderFillResults,\n LibFillResults.FillResults memory feeOrderFillResults\n );\n}\n", - "2.0.0/extensions/Forwarder/libs/LibConstants.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../../protocol/Exchange/interfaces/IExchange.sol\";\nimport \"../../../tokens/EtherToken/IEtherToken.sol\";\nimport \"../../../tokens/ERC20Token/IERC20Token.sol\";\n\n\ncontract LibConstants {\n\n bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256(\"ERC20Token(address)\"));\n bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256(\"ERC721Token(address,uint256)\"));\n uint256 constant internal MAX_UINT = 2**256 - 1;\n uint256 constant internal PERCENTAGE_DENOMINATOR = 10**18; \n uint256 constant internal MAX_FEE_PERCENTAGE = 5 * PERCENTAGE_DENOMINATOR / 100; // 5%\n uint256 constant internal MAX_WETH_FILL_PERCENTAGE = 95 * PERCENTAGE_DENOMINATOR / 100; // 95%\n \n // solhint-disable var-name-mixedcase\n IExchange internal EXCHANGE;\n IEtherToken internal ETHER_TOKEN;\n IERC20Token internal ZRX_TOKEN;\n bytes internal ZRX_ASSET_DATA;\n bytes internal WETH_ASSET_DATA;\n // solhint-enable var-name-mixedcase\n\n constructor (\n address _exchange,\n address _etherToken,\n address _zrxToken,\n bytes memory _zrxAssetData,\n bytes memory _wethAssetData\n )\n public\n {\n EXCHANGE = IExchange(_exchange);\n ETHER_TOKEN = IEtherToken(_etherToken);\n ZRX_TOKEN = IERC20Token(_zrxToken);\n ZRX_ASSET_DATA = _zrxAssetData;\n WETH_ASSET_DATA = _wethAssetData;\n }\n}\n", + "2.0.0/extensions/Forwarder/libs/LibConstants.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../../utils/LibBytes/LibBytes.sol\";\nimport \"../../../protocol/Exchange/interfaces/IExchange.sol\";\nimport \"../../../tokens/EtherToken/IEtherToken.sol\";\nimport \"../../../tokens/ERC20Token/IERC20Token.sol\";\n\n\ncontract LibConstants {\n\n using LibBytes for bytes;\n\n bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256(\"ERC20Token(address)\"));\n bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256(\"ERC721Token(address,uint256)\"));\n uint256 constant internal MAX_UINT = 2**256 - 1;\n uint256 constant internal PERCENTAGE_DENOMINATOR = 10**18; \n uint256 constant internal MAX_FEE_PERCENTAGE = 5 * PERCENTAGE_DENOMINATOR / 100; // 5%\n uint256 constant internal MAX_WETH_FILL_PERCENTAGE = 95 * PERCENTAGE_DENOMINATOR / 100; // 95%\n \n // solhint-disable var-name-mixedcase\n IExchange internal EXCHANGE;\n IEtherToken internal ETHER_TOKEN;\n IERC20Token internal ZRX_TOKEN;\n bytes internal ZRX_ASSET_DATA;\n bytes internal WETH_ASSET_DATA;\n // solhint-enable var-name-mixedcase\n\n constructor (\n address _exchange,\n bytes memory _zrxAssetData,\n bytes memory _wethAssetData\n )\n public\n {\n EXCHANGE = IExchange(_exchange);\n ZRX_ASSET_DATA = _zrxAssetData;\n WETH_ASSET_DATA = _wethAssetData;\n\n address etherToken = _wethAssetData.readAddress(16);\n address zrxToken = _zrxAssetData.readAddress(16);\n ETHER_TOKEN = IEtherToken(etherToken);\n ZRX_TOKEN = IERC20Token(zrxToken);\n }\n}\n", "2.0.0/extensions/Forwarder/mixins/MAssets.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../interfaces/IAssets.sol\";\n\n\ncontract MAssets is\n IAssets\n{\n\n /// @dev Transfers given amount of asset to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferAssetToSender(\n bytes memory assetData,\n uint256 amount\n )\n internal;\n\n /// @dev Decodes ERC20 assetData and transfers given amount to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferERC20Token(\n bytes memory assetData,\n uint256 amount\n )\n internal;\n\n /// @dev Decodes ERC721 assetData and transfers given amount to sender.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param amount Amount of asset to transfer to sender.\n function transferERC721Token(\n bytes memory assetData,\n uint256 amount\n )\n internal;\n}\n", "2.0.0/extensions/Forwarder/mixins/MExchangeWrapper.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../../protocol/Exchange/libs/LibFillResults.sol\";\n\n\ncontract MExchangeWrapper {\n\n /// @dev Fills the input order.\n /// Returns false if the transaction would otherwise revert.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderNoThrow(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (LibFillResults.FillResults memory fillResults);\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH has been sold by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param wethSellAmount Desired amount of WETH to sell.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellWeth(\n LibOrder.Order[] memory orders,\n uint256 wethSellAmount,\n bytes[] memory signatures\n )\n internal\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.\n /// Returns false if the transaction would otherwise revert.\n /// The asset being sold by taker must always be WETH.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyExactAmountWithWeth(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n internal\n returns (LibFillResults.FillResults memory totalFillResults);\n\n /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account ZRX fees for each order. This will guarantee\n /// that at least zrxBuyAmount of ZRX is purchased (sometimes slightly over due to rounding issues).\n /// It is possible that a request to buy 200 ZRX will require purchasing 202 ZRX\n /// as 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases.\n /// The asset being sold by taker must always be WETH. \n /// @param orders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset.\n /// @param zrxBuyAmount Desired amount of ZRX to buy.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return totalFillResults Amounts filled and fees paid by maker and taker.\n function marketBuyExactZrxWithWeth(\n LibOrder.Order[] memory orders,\n uint256 zrxBuyAmount,\n bytes[] memory signatures\n )\n internal\n returns (LibFillResults.FillResults memory totalFillResults);\n}\n", "2.0.0/extensions/Forwarder/mixins/MWeth.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract MWeth {\n\n /// @dev Converts message call's ETH value into WETH.\n function convertEthToWeth()\n internal;\n\n /// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.\n /// Refunds any excess ETH to msg.sender.\n /// @param wethSoldExcludingFeeOrders Amount of WETH sold when filling primary orders.\n /// @param wethSoldForZrx Amount of WETH sold when purchasing ZRX required for primary order fees.\n /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.\n /// @param feeRecipient Address that will receive ETH when orders are filled.\n function transferEthFeeAndRefund(\n uint256 wethSoldExcludingFeeOrders,\n uint256 wethSoldForZrx,\n uint256 feePercentage,\n address feeRecipient\n )\n internal;\n}\n", - "2.0.0/extensions/OrderValidator/OrderValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../protocol/Exchange/interfaces/IExchange.sol\";\nimport \"../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../tokens/ERC20Token/IERC20Token.sol\";\nimport \"../../tokens/ERC721Token/IERC721Token.sol\";\nimport \"../../utils/LibBytes/LibBytes.sol\";\n\n\ncontract OrderValidator {\n\n bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256(\"ERC20Token(address)\"));\n bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256(\"ERC721Token(address,uint256)\"));\n\n using LibBytes for bytes;\n\n struct TraderInfo {\n uint256 makerBalance; // Maker's balance of makerAsset\n uint256 makerAllowance; // Maker's allowance to corresponding AssetProxy\n uint256 takerBalance; // Taker's balance of takerAsset\n uint256 takerAllowance; // Taker's allowance to corresponding AssetProxy\n uint256 makerZrxBalance; // Maker's balance of ZRX\n uint256 makerZrxAllowance; // Maker's allowance of ZRX to ERC20Proxy\n uint256 takerZrxBalance; // Taker's balance of ZRX\n uint256 takerZrxAllowance; // Taker's allowance of ZRX to ERC20Proxy\n }\n\n // solhint-disable var-name-mixedcase\n IExchange internal EXCHANGE;\n bytes internal ZRX_ASSET_DATA;\n // solhint-enable var-name-mixedcase\n\n constructor (address _exchange, bytes memory _zrxAssetData)\n public\n {\n EXCHANGE = IExchange(_exchange);\n ZRX_ASSET_DATA = _zrxAssetData;\n }\n\n /// @dev Fetches information for order and maker/taker of order.\n /// @param order The order structure.\n /// @param takerAddress Address that will be filling the order.\n /// @return OrderInfo and TraderInfo instances for given order.\n function getOrderAndTraderInfo(LibOrder.Order memory order, address takerAddress)\n public\n view\n returns (LibOrder.OrderInfo memory orderInfo, TraderInfo memory traderInfo)\n {\n orderInfo = EXCHANGE.getOrderInfo(order);\n traderInfo = getTraderInfo(order, takerAddress);\n return (orderInfo, traderInfo);\n }\n\n /// @dev Fetches information for all passed in orders and the makers/takers of each order.\n /// @param orders Array of order specifications.\n /// @param takerAddresses Array of taker addresses corresponding to each order.\n /// @return Arrays of OrderInfo and TraderInfo instances that correspond to each order.\n function getOrdersAndTradersInfo(LibOrder.Order[] memory orders, address[] memory takerAddresses)\n public\n view\n returns (LibOrder.OrderInfo[] memory ordersInfo, TraderInfo[] memory tradersInfo)\n {\n ordersInfo = EXCHANGE.getOrdersInfo(orders);\n tradersInfo = getTradersInfo(orders, takerAddresses);\n return (ordersInfo, tradersInfo);\n }\n\n /// @dev Fetches balance and allowances for maker and taker of order.\n /// @param order The order structure.\n /// @param takerAddress Address that will be filling the order.\n /// @return Balances and allowances of maker and taker of order.\n function getTraderInfo(LibOrder.Order memory order, address takerAddress)\n public\n view\n returns (TraderInfo memory traderInfo)\n {\n (traderInfo.makerBalance, traderInfo.makerAllowance) = getBalanceAndAllowance(order.makerAddress, order.makerAssetData);\n (traderInfo.takerBalance, traderInfo.takerAllowance) = getBalanceAndAllowance(takerAddress, order.takerAssetData);\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n (traderInfo.makerZrxBalance, traderInfo.makerZrxAllowance) = getBalanceAndAllowance(order.makerAddress, zrxAssetData);\n (traderInfo.takerZrxBalance, traderInfo.takerZrxAllowance) = getBalanceAndAllowance(takerAddress, zrxAssetData);\n return traderInfo;\n }\n\n /// @dev Fetches balances and allowances of maker and taker for each provided order.\n /// @param orders Array of order specifications.\n /// @param takerAddresses Array of taker addresses corresponding to each order.\n /// @return Array of balances and allowances for maker and taker of each order.\n function getTradersInfo(LibOrder.Order[] memory orders, address[] memory takerAddresses)\n public\n view\n returns (TraderInfo[] memory)\n {\n uint256 ordersLength = orders.length;\n TraderInfo[] memory tradersInfo = new TraderInfo[](ordersLength);\n for (uint256 i = 0; i != ordersLength; i++) {\n tradersInfo[i] = getTraderInfo(orders[i], takerAddresses[i]);\n }\n return tradersInfo;\n }\n\n /// @dev Fetches token balances and allowances of an address to given assetProxy. Supports ERC20 and ERC721.\n /// @param target Address to fetch balances and allowances of.\n /// @param assetData Encoded data that can be decoded by a specified proxy contract when transferring asset.\n /// @return Balance of asset and allowance set to given proxy of asset.\n /// For ERC721 tokens, these values will always be 1 or 0.\n function getBalanceAndAllowance(address target, bytes memory assetData)\n public\n view\n returns (uint256 balance, uint256 allowance)\n {\n bytes4 assetProxyId = assetData.readBytes4(0);\n address token = assetData.readAddress(16);\n address assetProxy = EXCHANGE.getAssetProxy(assetProxyId);\n\n if (assetProxyId == ERC20_DATA_ID) {\n // Query balance\n balance = IERC20Token(token).balanceOf(target);\n\n // Query allowance\n allowance = IERC20Token(token).allowance(target, assetProxy);\n } else if (assetProxyId == ERC721_DATA_ID) {\n uint256 tokenId = assetData.readUint256(36);\n\n // Query owner of tokenId\n address owner = getERC721TokenOwner(token, tokenId);\n\n // Set balance to 1 if tokenId is owned by target\n balance = target == owner ? 1 : 0;\n\n // Check if ERC721Proxy is approved to spend tokenId\n bool isApproved = IERC721Token(token).isApprovedForAll(target, assetProxy) || IERC721Token(token).getApproved(tokenId) == assetProxy;\n \n // Set alowance to 1 if ERC721Proxy is approved to spend tokenId\n allowance = isApproved ? 1 : 0;\n } else {\n revert(\"UNSUPPORTED_ASSET_PROXY\");\n }\n return (balance, allowance);\n }\n\n /// @dev Calls `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token.\n /// @param token Address of ERC721 token.\n /// @param tokenId The identifier for the specific NFT.\n /// @return Owner of tokenId or null address if unowned.\n function getERC721TokenOwner(address token, uint256 tokenId)\n public\n view\n returns (address owner)\n {\n assembly {\n // load free memory pointer\n let cdStart := mload(64)\n\n // bytes4(keccak256(ownerOf(uint256))) = 0x6352211e\n mstore(cdStart, 0x6352211e00000000000000000000000000000000000000000000000000000000)\n mstore(add(cdStart, 4), tokenId)\n\n // staticcall `ownerOf(tokenId)`\n // `ownerOf` will revert if tokenId is not owned\n let success := staticcall(\n gas, // forward all gas\n token, // call token contract\n cdStart, // start of calldata\n 36, // length of input is 36 bytes\n cdStart, // write output over input\n 32 // size of output is 32 bytes\n )\n\n // Success implies that tokenId is owned\n // Copy owner from return data if successful\n if success {\n owner := mload(cdStart)\n } \n }\n\n // Owner initialized to address(0), no need to modify if call is unsuccessful\n return owner;\n }\n}\n", + "2.0.0/extensions/OrderValidator/OrderValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../protocol/Exchange/interfaces/IExchange.sol\";\nimport \"../../protocol/Exchange/libs/LibOrder.sol\";\nimport \"../../tokens/ERC20Token/IERC20Token.sol\";\nimport \"../../tokens/ERC721Token/IERC721Token.sol\";\nimport \"../../utils/LibBytes/LibBytes.sol\";\n\n\ncontract OrderValidator {\n\n bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256(\"ERC20Token(address)\"));\n bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256(\"ERC721Token(address,uint256)\"));\n\n using LibBytes for bytes;\n\n struct TraderInfo {\n uint256 makerBalance; // Maker's balance of makerAsset\n uint256 makerAllowance; // Maker's allowance to corresponding AssetProxy\n uint256 takerBalance; // Taker's balance of takerAsset\n uint256 takerAllowance; // Taker's allowance to corresponding AssetProxy\n uint256 makerZrxBalance; // Maker's balance of ZRX\n uint256 makerZrxAllowance; // Maker's allowance of ZRX to ERC20Proxy\n uint256 takerZrxBalance; // Taker's balance of ZRX\n uint256 takerZrxAllowance; // Taker's allowance of ZRX to ERC20Proxy\n }\n\n // solhint-disable var-name-mixedcase\n IExchange internal EXCHANGE;\n bytes internal ZRX_ASSET_DATA;\n // solhint-enable var-name-mixedcase\n\n constructor (address _exchange, bytes memory _zrxAssetData)\n public\n {\n EXCHANGE = IExchange(_exchange);\n ZRX_ASSET_DATA = _zrxAssetData;\n }\n\n /// @dev Fetches information for order and maker/taker of order.\n /// @param order The order structure.\n /// @param takerAddress Address that will be filling the order.\n /// @return OrderInfo and TraderInfo instances for given order.\n function getOrderAndTraderInfo(LibOrder.Order memory order, address takerAddress)\n public\n view\n returns (LibOrder.OrderInfo memory orderInfo, TraderInfo memory traderInfo)\n {\n orderInfo = EXCHANGE.getOrderInfo(order);\n traderInfo = getTraderInfo(order, takerAddress);\n return (orderInfo, traderInfo);\n }\n\n /// @dev Fetches information for all passed in orders and the makers/takers of each order.\n /// @param orders Array of order specifications.\n /// @param takerAddresses Array of taker addresses corresponding to each order.\n /// @return Arrays of OrderInfo and TraderInfo instances that correspond to each order.\n function getOrdersAndTradersInfo(LibOrder.Order[] memory orders, address[] memory takerAddresses)\n public\n view\n returns (LibOrder.OrderInfo[] memory ordersInfo, TraderInfo[] memory tradersInfo)\n {\n ordersInfo = EXCHANGE.getOrdersInfo(orders);\n tradersInfo = getTradersInfo(orders, takerAddresses);\n return (ordersInfo, tradersInfo);\n }\n\n /// @dev Fetches balance and allowances for maker and taker of order.\n /// @param order The order structure.\n /// @param takerAddress Address that will be filling the order.\n /// @return Balances and allowances of maker and taker of order.\n function getTraderInfo(LibOrder.Order memory order, address takerAddress)\n public\n view\n returns (TraderInfo memory traderInfo)\n {\n (traderInfo.makerBalance, traderInfo.makerAllowance) = getBalanceAndAllowance(order.makerAddress, order.makerAssetData);\n (traderInfo.takerBalance, traderInfo.takerAllowance) = getBalanceAndAllowance(takerAddress, order.takerAssetData);\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n (traderInfo.makerZrxBalance, traderInfo.makerZrxAllowance) = getBalanceAndAllowance(order.makerAddress, zrxAssetData);\n (traderInfo.takerZrxBalance, traderInfo.takerZrxAllowance) = getBalanceAndAllowance(takerAddress, zrxAssetData);\n return traderInfo;\n }\n\n /// @dev Fetches balances and allowances of maker and taker for each provided order.\n /// @param orders Array of order specifications.\n /// @param takerAddresses Array of taker addresses corresponding to each order.\n /// @return Array of balances and allowances for maker and taker of each order.\n function getTradersInfo(LibOrder.Order[] memory orders, address[] memory takerAddresses)\n public\n view\n returns (TraderInfo[] memory)\n {\n uint256 ordersLength = orders.length;\n TraderInfo[] memory tradersInfo = new TraderInfo[](ordersLength);\n for (uint256 i = 0; i != ordersLength; i++) {\n tradersInfo[i] = getTraderInfo(orders[i], takerAddresses[i]);\n }\n return tradersInfo;\n }\n\n /// @dev Fetches token balances and allowances of an address to given assetProxy. Supports ERC20 and ERC721.\n /// @param target Address to fetch balances and allowances of.\n /// @param assetData Encoded data that can be decoded by a specified proxy contract when transferring asset.\n /// @return Balance of asset and allowance set to given proxy of asset.\n /// For ERC721 tokens, these values will always be 1 or 0.\n function getBalanceAndAllowance(address target, bytes memory assetData)\n public\n view\n returns (uint256 balance, uint256 allowance)\n {\n bytes4 assetProxyId = assetData.readBytes4(0);\n address token = assetData.readAddress(16);\n address assetProxy = EXCHANGE.getAssetProxy(assetProxyId);\n\n if (assetProxyId == ERC20_DATA_ID) {\n // Query balance\n balance = IERC20Token(token).balanceOf(target);\n\n // Query allowance\n allowance = IERC20Token(token).allowance(target, assetProxy);\n } else if (assetProxyId == ERC721_DATA_ID) {\n uint256 tokenId = assetData.readUint256(36);\n\n // Query owner of tokenId\n address owner = getERC721TokenOwner(token, tokenId);\n\n // Set balance to 1 if tokenId is owned by target\n balance = target == owner ? 1 : 0;\n\n // Check if ERC721Proxy is approved to spend tokenId\n bool isApproved = IERC721Token(token).isApprovedForAll(target, assetProxy) || IERC721Token(token).getApproved(tokenId) == assetProxy;\n \n // Set alowance to 1 if ERC721Proxy is approved to spend tokenId\n allowance = isApproved ? 1 : 0;\n } else {\n revert(\"UNSUPPORTED_ASSET_PROXY\");\n }\n return (balance, allowance);\n }\n\n /// @dev Fetches token balances and allowances of an address for each given assetProxy. Supports ERC20 and ERC721.\n /// @param target Address to fetch balances and allowances of.\n /// @param assetData Array of encoded byte arrays that can be decoded by a specified proxy contract when transferring asset.\n /// @return Balances and allowances of assets.\n /// For ERC721 tokens, these values will always be 1 or 0.\n function getBalancesAndAllowances(address target, bytes[] memory assetData)\n public\n view\n returns (uint256[] memory, uint256[] memory)\n {\n uint256 length = assetData.length;\n uint256[] memory balances = new uint256[](length);\n uint256[] memory allowances = new uint256[](length);\n for (uint256 i = 0; i != length; i++) {\n (balances[i], allowances[i]) = getBalanceAndAllowance(target, assetData[i]);\n }\n return (balances, allowances);\n }\n\n /// @dev Calls `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token.\n /// @param token Address of ERC721 token.\n /// @param tokenId The identifier for the specific NFT.\n /// @return Owner of tokenId or null address if unowned.\n function getERC721TokenOwner(address token, uint256 tokenId)\n public\n view\n returns (address owner)\n {\n assembly {\n // load free memory pointer\n let cdStart := mload(64)\n\n // bytes4(keccak256(ownerOf(uint256))) = 0x6352211e\n mstore(cdStart, 0x6352211e00000000000000000000000000000000000000000000000000000000)\n mstore(add(cdStart, 4), tokenId)\n\n // staticcall `ownerOf(tokenId)`\n // `ownerOf` will revert if tokenId is not owned\n let success := staticcall(\n gas, // forward all gas\n token, // call token contract\n cdStart, // start of calldata\n 36, // length of input is 36 bytes\n cdStart, // write output over input\n 32 // size of output is 32 bytes\n )\n\n // Success implies that tokenId is owned\n // Copy owner from return data if successful\n if success {\n owner := mload(cdStart)\n } \n }\n\n // Owner initialized to address(0), no need to modify if call is unsuccessful\n return owner;\n }\n}\n", + "2.0.0/protocol/AssetProxy/ERC20Proxy.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"./MixinAuthorizable.sol\";\n\n\ncontract ERC20Proxy is\n MixinAuthorizable\n{\n // Id of this proxy.\n bytes4 constant internal PROXY_ID = bytes4(keccak256(\"ERC20Token(address)\"));\n \n // solhint-disable-next-line payable-fallback\n function () \n external\n {\n assembly {\n // The first 4 bytes of calldata holds the function selector\n let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)\n\n // `transferFrom` will be called with the following parameters:\n // assetData Encoded byte array.\n // from Address to transfer asset from.\n // to Address to transfer asset to.\n // amount Amount of asset to transfer.\n // bytes4(keccak256(\"transferFrom(bytes,address,address,uint256)\")) = 0xa85e59e4\n if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {\n\n // To lookup a value in a mapping, we load from the storage location keccak256(k, p),\n // where k is the key left padded to 32 bytes and p is the storage slot\n let start := mload(64)\n mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff))\n mstore(add(start, 32), authorized_slot)\n\n // Revert if authorized[msg.sender] == false\n if iszero(sload(keccak256(start, 64))) {\n // Revert with `Error(\"SENDER_NOT_AUTHORIZED\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n \n /////// Token contract address ///////\n // The token address is found as follows:\n // * It is stored at offset 4 in `assetData` contents.\n // * This is stored at offset 32 from `assetData`.\n // * The offset to `assetData` from Params is stored at offset\n // 4 in calldata.\n // * The offset of Params in calldata is 4.\n // So we read location 4 and add 32 + 4 + 4 to it.\n let token := calldataload(add(calldataload(4), 40))\n \n /////// Setup Header Area ///////\n // This area holds the 4-byte `transferFrom` selector.\n // Any trailing data in transferFromSelector will be\n // overwritten in the next `mstore` call.\n mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n \n /////// Setup Params Area ///////\n // We copy the fields `from`, `to` and `amount` in bulk\n // from our own calldata to the new calldata.\n calldatacopy(4, 36, 96)\n\n /////// Call `token.transferFrom` using the calldata ///////\n let success := call(\n gas, // forward all gas\n token, // call address of token contract\n 0, // don't send any ETH\n 0, // pointer to start of input\n 100, // length of input\n 0, // write output over input\n 32 // output size should be 32 bytes\n )\n\n /////// Check return data. ///////\n // If there is no return data, we assume the token incorrectly\n // does not return a bool. In this case we expect it to revert\n // on failure, which was handled above.\n // If the token does return data, we require that it is a single\n // nonzero 32 bytes value.\n // So the transfer succeeded if the call succeeded and either\n // returned nothing, or returned a non-zero 32 byte value. \n success := and(success, or(\n iszero(returndatasize),\n and(\n eq(returndatasize, 32),\n gt(mload(0), 0)\n )\n ))\n if success {\n return(0, 0)\n }\n \n // Revert with `Error(\"TRANSFER_FAILED\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n\n // Revert if undefined function is called\n revert(0, 0)\n }\n }\n\n /// @dev Gets the proxy id associated with the proxy address.\n /// @return Proxy id.\n function getProxyId()\n external\n pure\n returns (bytes4)\n {\n return PROXY_ID;\n }\n}\n", + "2.0.0/protocol/AssetProxy/ERC721Proxy.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"./MixinAuthorizable.sol\";\n\n\ncontract ERC721Proxy is\n MixinAuthorizable\n{\n // Id of this proxy.\n bytes4 constant internal PROXY_ID = bytes4(keccak256(\"ERC721Token(address,uint256)\"));\n\n // solhint-disable-next-line payable-fallback\n function () \n external\n {\n assembly {\n // The first 4 bytes of calldata holds the function selector\n let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)\n\n // `transferFrom` will be called with the following parameters:\n // assetData Encoded byte array.\n // from Address to transfer asset from.\n // to Address to transfer asset to.\n // amount Amount of asset to transfer.\n // bytes4(keccak256(\"transferFrom(bytes,address,address,uint256)\")) = 0xa85e59e4\n if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {\n\n // To lookup a value in a mapping, we load from the storage location keccak256(k, p),\n // where k is the key left padded to 32 bytes and p is the storage slot\n let start := mload(64)\n mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff))\n mstore(add(start, 32), authorized_slot)\n\n // Revert if authorized[msg.sender] == false\n if iszero(sload(keccak256(start, 64))) {\n // Revert with `Error(\"SENDER_NOT_AUTHORIZED\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n\n // `transferFrom`.\n // The function is marked `external`, so no abi decodeding is done for\n // us. Instead, we expect the `calldata` memory to contain the\n // following:\n //\n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 4 * 32 | function parameters: |\n // | | 4 | | 1. offset to assetData (*) |\n // | | 36 | | 2. from |\n // | | 68 | | 3. to |\n // | | 100 | | 4. amount |\n // | Data | | | assetData: |\n // | | 132 | 32 | assetData Length |\n // | | 164 | ** | assetData Contents |\n //\n // (*): offset is computed from start of function parameters, so offset\n // by an additional 4 bytes in the calldata.\n //\n // WARNING: The ABIv2 specification allows additional padding between\n // the Params and Data section. This will result in a larger\n // offset to assetData.\n\n // Asset data itself is encoded as follows:\n //\n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 2 * 32 | function parameters: |\n // | | 4 | 12 + 20 | 1. token address |\n // | | 36 | | 2. tokenId |\n \n // We construct calldata for the `token.transferFrom` ABI.\n // The layout of this calldata is in the table below.\n // \n // | Area | Offset | Length | Contents |\n // |----------|--------|---------|-------------------------------------|\n // | Header | 0 | 4 | function selector |\n // | Params | | 3 * 32 | function parameters: |\n // | | 4 | | 1. from |\n // | | 36 | | 2. to |\n // | | 68 | | 3. tokenId |\n\n // There exists only 1 of each token.\n // require(amount == 1, \"INVALID_AMOUNT\")\n if sub(calldataload(100), 1) {\n // Revert with `Error(\"INVALID_AMOUNT\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000e494e56414c49445f414d4f554e540000000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n\n /////// Setup Header Area ///////\n // This area holds the 4-byte `transferFrom` selector.\n // Any trailing data in transferFromSelector will be\n // overwritten in the next `mstore` call.\n mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n \n /////// Setup Params Area ///////\n // We copy the fields `from` and `to` in bulk\n // from our own calldata to the new calldata.\n calldatacopy(4, 36, 64)\n\n // Copy `tokenId` field from our own calldata to the new calldata.\n let assetDataOffset := calldataload(4)\n calldatacopy(68, add(assetDataOffset, 72), 32)\n\n /////// Call `token.transferFrom` using the calldata ///////\n let token := calldataload(add(assetDataOffset, 40))\n let success := call(\n gas, // forward all gas\n token, // call address of token contract\n 0, // don't send any ETH\n 0, // pointer to start of input\n 100, // length of input\n 0, // write output to null\n 0 // output size is 0 bytes\n )\n if success {\n return(0, 0)\n }\n \n // Revert with `Error(\"TRANSFER_FAILED\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n\n // Revert if undefined function is called\n revert(0, 0)\n }\n }\n\n /// @dev Gets the proxy id associated with the proxy address.\n /// @return Proxy id.\n function getProxyId()\n external\n pure\n returns (bytes4)\n {\n return PROXY_ID;\n }\n}\n", + "2.0.0/protocol/AssetProxy/MixinAuthorizable.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/Ownable/Ownable.sol\";\nimport \"./mixins/MAuthorizable.sol\";\n\n\ncontract MixinAuthorizable is\n Ownable,\n MAuthorizable\n{\n\n /// @dev Only authorized addresses can invoke functions with this modifier.\n modifier onlyAuthorized {\n require(\n authorized[msg.sender],\n \"SENDER_NOT_AUTHORIZED\"\n );\n _;\n }\n\n mapping (address => bool) public authorized;\n address[] public authorities;\n\n /// @dev Authorizes an address.\n /// @param target Address to authorize.\n function addAuthorizedAddress(address target)\n external\n onlyOwner\n {\n require(\n !authorized[target],\n \"TARGET_ALREADY_AUTHORIZED\"\n );\n\n authorized[target] = true;\n authorities.push(target);\n emit AuthorizedAddressAdded(target, msg.sender);\n }\n\n /// @dev Removes authorizion of an address.\n /// @param target Address to remove authorization from.\n function removeAuthorizedAddress(address target)\n external\n onlyOwner\n {\n require(\n authorized[target],\n \"TARGET_NOT_AUTHORIZED\"\n );\n\n delete authorized[target];\n for (uint256 i = 0; i < authorities.length; i++) {\n if (authorities[i] == target) {\n authorities[i] = authorities[authorities.length - 1];\n authorities.length -= 1;\n break;\n }\n }\n emit AuthorizedAddressRemoved(target, msg.sender);\n }\n\n /// @dev Removes authorizion of an address.\n /// @param target Address to remove authorization from.\n /// @param index Index of target in authorities array.\n function removeAuthorizedAddressAtIndex(\n address target,\n uint256 index\n )\n external\n onlyOwner\n {\n require(\n authorized[target],\n \"TARGET_NOT_AUTHORIZED\"\n );\n require(\n index < authorities.length,\n \"INDEX_OUT_OF_BOUNDS\"\n );\n require(\n authorities[index] == target,\n \"AUTHORIZED_ADDRESS_MISMATCH\"\n );\n\n delete authorized[target];\n authorities[index] = authorities[authorities.length - 1];\n authorities.length -= 1;\n emit AuthorizedAddressRemoved(target, msg.sender);\n }\n\n /// @dev Gets all authorized addresses.\n /// @return Array of authorized addresses.\n function getAuthorizedAddresses()\n external\n view\n returns (address[] memory)\n {\n return authorities;\n }\n}\n", "2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"./IAuthorizable.sol\";\n\n\ncontract IAssetProxy is\n IAuthorizable\n{\n\n /// @dev Transfers assets. Either succeeds or throws.\n /// @param assetData Byte array encoded for the respective asset proxy.\n /// @param from Address to transfer asset from.\n /// @param to Address to transfer asset to.\n /// @param amount Amount of asset to transfer.\n function transferFrom(\n bytes assetData,\n address from,\n address to,\n uint256 amount\n )\n external;\n \n /// @dev Gets the proxy id associated with the proxy address.\n /// @return Proxy id.\n function getProxyId()\n external\n pure\n returns (bytes4);\n}\n", "2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../../utils/Ownable/IOwnable.sol\";\n\n\ncontract IAuthorizable is\n IOwnable\n{\n\n /// @dev Authorizes an address.\n /// @param target Address to authorize.\n function addAuthorizedAddress(address target)\n external;\n\n /// @dev Removes authorizion of an address.\n /// @param target Address to remove authorization from.\n function removeAuthorizedAddress(address target)\n external;\n\n /// @dev Removes authorizion of an address.\n /// @param target Address to remove authorization from.\n /// @param index Index of target in authorities array.\n function removeAuthorizedAddressAtIndex(\n address target,\n uint256 index\n )\n external;\n \n /// @dev Gets all authorized addresses.\n /// @return Array of authorized addresses.\n function getAuthorizedAddresses()\n external\n view\n returns (address[] memory);\n}\n", + "2.0.0/protocol/AssetProxy/mixins/MAuthorizable.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../interfaces/IAuthorizable.sol\";\n\n\ncontract MAuthorizable is\n IAuthorizable\n{\n\n // Event logged when a new address is authorized.\n event AuthorizedAddressAdded(\n address indexed target,\n address indexed caller\n );\n\n // Event logged when a currently authorized address is unauthorized.\n event AuthorizedAddressRemoved(\n address indexed target,\n address indexed caller\n );\n\n /// @dev Only authorized addresses can invoke functions with this modifier.\n modifier onlyAuthorized { revert(); _; }\n}\n", "2.0.0/protocol/Exchange/Exchange.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./libs/LibConstants.sol\";\nimport \"./MixinExchangeCore.sol\";\nimport \"./MixinSignatureValidator.sol\";\nimport \"./MixinWrapperFunctions.sol\";\nimport \"./MixinAssetProxyDispatcher.sol\";\nimport \"./MixinTransactions.sol\";\nimport \"./MixinMatchOrders.sol\";\n\n\n// solhint-disable no-empty-blocks\ncontract Exchange is\n MixinExchangeCore,\n MixinMatchOrders,\n MixinSignatureValidator,\n MixinTransactions,\n MixinAssetProxyDispatcher,\n MixinWrapperFunctions\n{\n\n string constant public VERSION = \"2.0.1-alpha\";\n\n // Mixins are instantiated in the order they are inherited\n constructor (bytes memory _zrxAssetData)\n public\n LibConstants(_zrxAssetData) // @TODO: Remove when we deploy.\n MixinExchangeCore()\n MixinMatchOrders()\n MixinSignatureValidator()\n MixinTransactions()\n MixinAssetProxyDispatcher()\n MixinWrapperFunctions()\n {}\n}\n", - "2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/Ownable/Ownable.sol\";\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"./mixins/MAssetProxyDispatcher.sol\";\nimport \"../AssetProxy/interfaces/IAssetProxy.sol\";\n\n\ncontract MixinAssetProxyDispatcher is\n Ownable,\n MAssetProxyDispatcher\n{\n using LibBytes for bytes;\n \n // Mapping from Asset Proxy Id's to their respective Asset Proxy\n mapping (bytes4 => IAssetProxy) public assetProxies;\n\n /// @dev Registers an asset proxy to its asset proxy id.\n /// Once an asset proxy is registered, it cannot be unregistered.\n /// @param assetProxy Address of new asset proxy to register.\n function registerAssetProxy(address assetProxy)\n external\n onlyOwner\n {\n IAssetProxy assetProxyContract = IAssetProxy(assetProxy);\n\n // Ensure that no asset proxy exists with current id.\n bytes4 assetProxyId = assetProxyContract.getProxyId();\n address currentAssetProxy = assetProxies[assetProxyId];\n require(\n currentAssetProxy == address(0),\n \"ASSET_PROXY_ALREADY_EXISTS\"\n );\n\n // Add asset proxy and log registration.\n assetProxies[assetProxyId] = assetProxyContract;\n emit AssetProxyRegistered(\n assetProxyId,\n assetProxy\n );\n }\n\n /// @dev Gets an asset proxy.\n /// @param assetProxyId Id of the asset proxy.\n /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.\n function getAssetProxy(bytes4 assetProxyId)\n external\n view\n returns (address)\n {\n return assetProxies[assetProxyId];\n }\n\n /// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.\n /// @param assetData Byte array encoded for the asset.\n /// @param from Address to transfer token from.\n /// @param to Address to transfer token to.\n /// @param amount Amount of token to transfer.\n function dispatchTransferFrom(\n bytes memory assetData,\n address from,\n address to,\n uint256 amount\n )\n internal\n {\n // Do nothing if no amount should be transferred.\n if (amount > 0) {\n // Ensure assetData length is valid\n require(\n assetData.length > 3,\n \"LENGTH_GREATER_THAN_3_REQUIRED\"\n );\n \n // Lookup assetProxy\n bytes4 assetProxyId;\n assembly {\n assetProxyId := and(mload(\n add(assetData, 32)),\n 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000\n )\n }\n address assetProxy = assetProxies[assetProxyId];\n\n // Ensure that assetProxy exists\n require(\n assetProxy != address(0),\n \"ASSET_PROXY_DOES_NOT_EXIST\"\n );\n \n // We construct calldata for the `assetProxy.transferFrom` ABI.\n // The layout of this calldata is in the table below.\n // \n // | Area | Offset | Length | Contents |\n // | -------- |--------|---------|-------------------------------------------- |\n // | Header | 0 | 4 | function selector |\n // | Params | | 4 * 32 | function parameters: |\n // | | 4 | | 1. offset to assetData (*) |\n // | | 36 | | 2. from |\n // | | 68 | | 3. to |\n // | | 100 | | 4. amount |\n // | Data | | | assetData: |\n // | | 132 | 32 | assetData Length |\n // | | 164 | ** | assetData Contents |\n\n assembly {\n /////// Setup State ///////\n // `cdStart` is the start of the calldata for `assetProxy.transferFrom` (equal to free memory ptr).\n let cdStart := mload(64)\n // `dataAreaLength` is the total number of words needed to store `assetData`\n // As-per the ABI spec, this value is padded up to the nearest multiple of 32,\n // and includes 32-bytes for length.\n let dataAreaLength := and(add(mload(assetData), 63), 0xFFFFFFFFFFFE0)\n // `cdEnd` is the end of the calldata for `assetProxy.transferFrom`.\n let cdEnd := add(cdStart, add(132, dataAreaLength))\n\n \n /////// Setup Header Area ///////\n // This area holds the 4-byte `transferFromSelector`.\n // bytes4(keccak256(\"transferFrom(bytes,address,address,uint256)\")) = 0xa85e59e4\n mstore(cdStart, 0xa85e59e400000000000000000000000000000000000000000000000000000000)\n \n /////// Setup Params Area ///////\n // Each parameter is padded to 32-bytes. The entire Params Area is 128 bytes.\n // Notes:\n // 1. The offset to `assetData` is the length of the Params Area (128 bytes).\n // 2. A 20-byte mask is applied to addresses to zero-out the unused bytes.\n mstore(add(cdStart, 4), 128)\n mstore(add(cdStart, 36), and(from, 0xffffffffffffffffffffffffffffffffffffffff))\n mstore(add(cdStart, 68), and(to, 0xffffffffffffffffffffffffffffffffffffffff))\n mstore(add(cdStart, 100), amount)\n \n /////// Setup Data Area ///////\n // This area holds `assetData`.\n let dataArea := add(cdStart, 132)\n // solhint-disable-next-line no-empty-blocks\n for {} lt(dataArea, cdEnd) {} {\n mstore(dataArea, mload(assetData))\n dataArea := add(dataArea, 32)\n assetData := add(assetData, 32)\n }\n\n /////// Call `assetProxy.transferFrom` using the constructed calldata ///////\n let success := call(\n gas, // forward all gas\n assetProxy, // call address of asset proxy\n 0, // don't send any ETH\n cdStart, // pointer to start of input\n sub(cdEnd, cdStart), // length of input \n cdStart, // write output over input\n 512 // reserve 512 bytes for output\n )\n if iszero(success) {\n revert(cdStart, returndatasize())\n }\n }\n }\n }\n}\n", - "2.0.0/protocol/Exchange/MixinExchangeCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./libs/LibConstants.sol\";\nimport \"./libs/LibFillResults.sol\";\nimport \"./libs/LibOrder.sol\";\nimport \"./libs/LibMath.sol\";\nimport \"./mixins/MExchangeCore.sol\";\nimport \"./mixins/MSignatureValidator.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./mixins/MAssetProxyDispatcher.sol\";\n\n\ncontract MixinExchangeCore is\n LibConstants,\n LibMath,\n LibOrder,\n LibFillResults,\n MAssetProxyDispatcher,\n MExchangeCore,\n MSignatureValidator,\n MTransactions\n{\n // Mapping of orderHash => amount of takerAsset already bought by maker\n mapping (bytes32 => uint256) public filled;\n\n // Mapping of orderHash => cancelled\n mapping (bytes32 => bool) public cancelled;\n\n // Mapping of makerAddress => senderAddress => lowest salt an order can have in order to be fillable\n // Orders with specified senderAddress and with a salt less than their epoch are considered cancelled\n mapping (address => mapping (address => uint256)) public orderEpoch;\n\n /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch\n /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress).\n /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.\n function cancelOrdersUpTo(uint256 targetOrderEpoch)\n external\n {\n address makerAddress = getCurrentContextAddress();\n // If this function is called via `executeTransaction`, we only update the orderEpoch for the makerAddress/msg.sender combination.\n // This allows external filter contracts to add rules to how orders are cancelled via this function.\n address senderAddress = makerAddress == msg.sender ? address(0) : msg.sender;\n\n // orderEpoch is initialized to 0, so to cancelUpTo we need salt + 1\n uint256 newOrderEpoch = targetOrderEpoch + 1; \n uint256 oldOrderEpoch = orderEpoch[makerAddress][senderAddress];\n\n // Ensure orderEpoch is monotonically increasing\n require(\n newOrderEpoch > oldOrderEpoch, \n \"INVALID_NEW_ORDER_EPOCH\"\n );\n\n // Update orderEpoch\n orderEpoch[makerAddress][senderAddress] = newOrderEpoch;\n emit CancelUpTo(makerAddress, senderAddress, newOrderEpoch);\n }\n\n /// @dev Fills the input order.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrder(\n Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n returns (FillResults memory fillResults)\n {\n // Fetch order info\n OrderInfo memory orderInfo = getOrderInfo(order);\n\n // Fetch taker address\n address takerAddress = getCurrentContextAddress();\n\n // Get amount of takerAsset to fill\n uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount);\n uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount);\n\n // Validate context\n assertValidFill(\n order,\n orderInfo,\n takerAddress,\n takerAssetFillAmount,\n takerAssetFilledAmount,\n signature\n );\n\n // Compute proportional fill amounts\n fillResults = calculateFillResults(order, takerAssetFilledAmount);\n\n // Update exchange internal state\n updateFilledState(\n order,\n takerAddress,\n orderInfo.orderHash,\n orderInfo.orderTakerAssetFilledAmount,\n fillResults\n );\n \n // Settle order\n settleOrder(order, takerAddress, fillResults);\n\n return fillResults;\n }\n\n /// @dev After calling, the order can not be filled anymore.\n /// Throws if order is invalid or sender does not have permission to cancel.\n /// @param order Order to cancel. Order must be OrderStatus.FILLABLE.\n function cancelOrder(Order memory order)\n public\n {\n // Fetch current order status\n OrderInfo memory orderInfo = getOrderInfo(order);\n\n // Validate context\n assertValidCancel(order, orderInfo);\n\n // Perform cancel\n updateCancelledState(order, orderInfo.orderHash);\n }\n\n /// @dev Gets information about an order: status, hash, and amount filled.\n /// @param order Order to gather information on.\n /// @return OrderInfo Information about the order and its state.\n /// See LibOrder.OrderInfo for a complete description.\n function getOrderInfo(Order memory order)\n public\n view\n returns (OrderInfo memory orderInfo)\n {\n // Compute the order hash\n orderInfo.orderHash = getOrderHash(order);\n\n // Fetch filled amount\n orderInfo.orderTakerAssetFilledAmount = filled[orderInfo.orderHash];\n\n // If order.makerAssetAmount is zero, we also reject the order.\n // While the Exchange contract handles them correctly, they create\n // edge cases in the supporting infrastructure because they have\n // an 'infinite' price when computed by a simple division.\n if (order.makerAssetAmount == 0) {\n orderInfo.orderStatus = uint8(OrderStatus.INVALID_MAKER_ASSET_AMOUNT);\n return orderInfo;\n }\n\n // If order.takerAssetAmount is zero, then the order will always\n // be considered filled because 0 == takerAssetAmount == orderTakerAssetFilledAmount\n // Instead of distinguishing between unfilled and filled zero taker\n // amount orders, we choose not to support them.\n if (order.takerAssetAmount == 0) {\n orderInfo.orderStatus = uint8(OrderStatus.INVALID_TAKER_ASSET_AMOUNT);\n return orderInfo;\n }\n\n // Validate order availability\n if (orderInfo.orderTakerAssetFilledAmount >= order.takerAssetAmount) {\n orderInfo.orderStatus = uint8(OrderStatus.FULLY_FILLED);\n return orderInfo;\n }\n\n // Validate order expiration\n // solhint-disable-next-line not-rely-on-time\n if (block.timestamp >= order.expirationTimeSeconds) {\n orderInfo.orderStatus = uint8(OrderStatus.EXPIRED);\n return orderInfo;\n }\n\n // Check if order has been cancelled\n if (cancelled[orderInfo.orderHash]) {\n orderInfo.orderStatus = uint8(OrderStatus.CANCELLED);\n return orderInfo;\n }\n if (orderEpoch[order.makerAddress][order.senderAddress] > order.salt) {\n orderInfo.orderStatus = uint8(OrderStatus.CANCELLED);\n return orderInfo;\n }\n\n // All other statuses are ruled out: order is Fillable\n orderInfo.orderStatus = uint8(OrderStatus.FILLABLE);\n return orderInfo;\n }\n\n /// @dev Updates state with results of a fill order.\n /// @param order that was filled.\n /// @param takerAddress Address of taker who filled the order.\n /// @param orderTakerAssetFilledAmount Amount of order already filled.\n function updateFilledState(\n Order memory order,\n address takerAddress,\n bytes32 orderHash,\n uint256 orderTakerAssetFilledAmount,\n FillResults memory fillResults\n )\n internal\n {\n // Update state\n filled[orderHash] = safeAdd(orderTakerAssetFilledAmount, fillResults.takerAssetFilledAmount);\n\n // Log order\n emit Fill(\n order.makerAddress,\n order.feeRecipientAddress,\n takerAddress,\n msg.sender,\n fillResults.makerAssetFilledAmount,\n fillResults.takerAssetFilledAmount,\n fillResults.makerFeePaid,\n fillResults.takerFeePaid,\n orderHash,\n order.makerAssetData,\n order.takerAssetData\n );\n }\n\n /// @dev Updates state with results of cancelling an order.\n /// State is only updated if the order is currently fillable.\n /// Otherwise, updating state would have no effect.\n /// @param order that was cancelled.\n /// @param orderHash Hash of order that was cancelled.\n function updateCancelledState(\n Order memory order,\n bytes32 orderHash\n )\n internal\n {\n // Perform cancel\n cancelled[orderHash] = true;\n\n // Log cancel\n emit Cancel(\n order.makerAddress,\n order.feeRecipientAddress,\n msg.sender,\n orderHash,\n order.makerAssetData,\n order.takerAssetData\n );\n }\n\n /// @dev Validates context for fillOrder. Succeeds or throws.\n /// @param order to be filled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n /// @param takerAddress Address of order taker.\n /// @param takerAssetFillAmount Desired amount of order to fill by taker.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @param signature Proof that the orders was created by its maker.\n function assertValidFill(\n Order memory order,\n OrderInfo memory orderInfo,\n address takerAddress,\n uint256 takerAssetFillAmount,\n uint256 takerAssetFilledAmount,\n bytes memory signature\n )\n internal\n view\n {\n // An order can only be filled if its status is FILLABLE.\n require(\n orderInfo.orderStatus == uint8(OrderStatus.FILLABLE),\n \"ORDER_UNFILLABLE\"\n );\n\n // Revert if fill amount is invalid\n require(\n takerAssetFillAmount != 0,\n \"INVALID_TAKER_AMOUNT\"\n );\n\n // Validate sender is allowed to fill this order\n if (order.senderAddress != address(0)) {\n require(\n order.senderAddress == msg.sender,\n \"INVALID_SENDER\"\n );\n }\n\n // Validate taker is allowed to fill this order\n if (order.takerAddress != address(0)) {\n require(\n order.takerAddress == takerAddress,\n \"INVALID_TAKER\"\n );\n }\n\n // Validate Maker signature (check only if first time seen)\n if (orderInfo.orderTakerAssetFilledAmount == 0) {\n require(\n isValidSignature(\n orderInfo.orderHash,\n order.makerAddress,\n signature\n ),\n \"INVALID_ORDER_SIGNATURE\"\n );\n }\n\n // Validate fill order rounding\n require(\n !isRoundingError(\n takerAssetFilledAmount,\n order.takerAssetAmount,\n order.makerAssetAmount\n ),\n \"ROUNDING_ERROR\"\n );\n }\n\n /// @dev Validates context for cancelOrder. Succeeds or throws.\n /// @param order to be cancelled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n function assertValidCancel(\n Order memory order,\n OrderInfo memory orderInfo\n )\n internal\n view\n {\n // Ensure order is valid\n // An order can only be cancelled if its status is FILLABLE.\n require(\n orderInfo.orderStatus == uint8(OrderStatus.FILLABLE),\n \"ORDER_UNFILLABLE\"\n );\n\n // Validate sender is allowed to cancel this order\n if (order.senderAddress != address(0)) {\n require(\n order.senderAddress == msg.sender,\n \"INVALID_SENDER\"\n );\n }\n\n // Validate transaction signed by maker\n address makerAddress = getCurrentContextAddress();\n require(\n order.makerAddress == makerAddress,\n \"INVALID_MAKER\"\n );\n }\n\n /// @dev Calculates amounts filled and fees paid by maker and taker.\n /// @param order to be filled.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @return fillResults Amounts filled and fees paid by maker and taker.\n function calculateFillResults(\n Order memory order,\n uint256 takerAssetFilledAmount\n )\n internal\n pure\n returns (FillResults memory fillResults)\n {\n // Compute proportional transfer amounts\n fillResults.takerAssetFilledAmount = takerAssetFilledAmount;\n fillResults.makerAssetFilledAmount = getPartialAmount(\n takerAssetFilledAmount,\n order.takerAssetAmount,\n order.makerAssetAmount\n );\n fillResults.makerFeePaid = getPartialAmount(\n takerAssetFilledAmount,\n order.takerAssetAmount,\n order.makerFee\n );\n fillResults.takerFeePaid = getPartialAmount(\n takerAssetFilledAmount,\n order.takerAssetAmount,\n order.takerFee\n );\n\n return fillResults;\n }\n\n /// @dev Settles an order by transferring assets between counterparties.\n /// @param order Order struct containing order specifications.\n /// @param takerAddress Address selling takerAsset and buying makerAsset.\n /// @param fillResults Amounts to be filled and fees paid by maker and taker.\n function settleOrder(\n LibOrder.Order memory order,\n address takerAddress,\n LibFillResults.FillResults memory fillResults\n )\n private\n {\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n dispatchTransferFrom(\n order.makerAssetData,\n order.makerAddress,\n takerAddress,\n fillResults.makerAssetFilledAmount\n );\n dispatchTransferFrom(\n order.takerAssetData,\n takerAddress,\n order.makerAddress,\n fillResults.takerAssetFilledAmount\n );\n dispatchTransferFrom(\n zrxAssetData,\n order.makerAddress,\n order.feeRecipientAddress,\n fillResults.makerFeePaid\n );\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n order.feeRecipientAddress,\n fillResults.takerFeePaid\n );\n }\n}\n", - "2.0.0/protocol/Exchange/MixinMatchOrders.sol": "/*\n Copyright 2018 ZeroEx Intl.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n http://www.apache.org/licenses/LICENSE-2.0\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./libs/LibConstants.sol\";\nimport \"./libs/LibMath.sol\";\nimport \"./libs/LibOrder.sol\";\nimport \"./libs/LibFillResults.sol\";\nimport \"./mixins/MExchangeCore.sol\";\nimport \"./mixins/MMatchOrders.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./mixins/MAssetProxyDispatcher.sol\";\n\n\ncontract MixinMatchOrders is\n LibConstants,\n LibMath,\n MAssetProxyDispatcher,\n MExchangeCore,\n MMatchOrders,\n MTransactions\n{\n /// @dev Match two complementary orders that have a profitable spread.\n /// Each order is filled at their respective price point. However, the calculations are\n /// carried out as though the orders are both being filled at the right order's price point.\n /// The profit made by the left order goes to the taker (who matched the two orders).\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n /// @param leftSignature Proof that order was created by the left maker.\n /// @param rightSignature Proof that order was created by the right maker.\n /// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders.\n function matchOrders(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n bytes memory leftSignature,\n bytes memory rightSignature\n )\n public\n returns (LibFillResults.MatchedFillResults memory matchedFillResults)\n {\n // We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData.\n // If this assumption isn't true, the match will fail at signature validation.\n rightOrder.makerAssetData = leftOrder.takerAssetData;\n rightOrder.takerAssetData = leftOrder.makerAssetData;\n\n // Get left & right order info\n LibOrder.OrderInfo memory leftOrderInfo = getOrderInfo(leftOrder);\n LibOrder.OrderInfo memory rightOrderInfo = getOrderInfo(rightOrder);\n\n // Fetch taker address\n address takerAddress = getCurrentContextAddress();\n\n // Either our context is valid or we revert\n assertValidMatch(leftOrder, rightOrder);\n\n // Compute proportional fill amounts\n matchedFillResults = calculateMatchedFillResults(\n leftOrder,\n rightOrder,\n leftOrderInfo.orderTakerAssetFilledAmount,\n rightOrderInfo.orderTakerAssetFilledAmount\n );\n\n // Validate fill contexts\n assertValidFill(\n leftOrder,\n leftOrderInfo,\n takerAddress,\n matchedFillResults.left.takerAssetFilledAmount,\n matchedFillResults.left.takerAssetFilledAmount,\n leftSignature\n );\n assertValidFill(\n rightOrder,\n rightOrderInfo,\n takerAddress,\n matchedFillResults.right.takerAssetFilledAmount,\n matchedFillResults.right.takerAssetFilledAmount,\n rightSignature\n );\n\n // Update exchange state\n updateFilledState(\n leftOrder,\n takerAddress,\n leftOrderInfo.orderHash,\n leftOrderInfo.orderTakerAssetFilledAmount,\n matchedFillResults.left\n );\n updateFilledState(\n rightOrder,\n takerAddress,\n rightOrderInfo.orderHash,\n rightOrderInfo.orderTakerAssetFilledAmount,\n matchedFillResults.right\n );\n \n // Settle matched orders. Succeeds or throws.\n settleMatchedOrders(\n leftOrder,\n rightOrder,\n takerAddress,\n matchedFillResults\n );\n\n return matchedFillResults;\n }\n\n /// @dev Validates context for matchOrders. Succeeds or throws.\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n function assertValidMatch(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder\n )\n internal\n pure\n {\n // Make sure there is a profitable spread.\n // There is a profitable spread iff the cost per unit bought (OrderA.MakerAmount/OrderA.TakerAmount) for each order is greater\n // than the profit per unit sold of the matched order (OrderB.TakerAmount/OrderB.MakerAmount).\n // This is satisfied by the equations below:\n // / >= / \n // AND\n // / >= / \n // These equations can be combined to get the following:\n require(\n safeMul(leftOrder.makerAssetAmount, rightOrder.makerAssetAmount) >=\n safeMul(leftOrder.takerAssetAmount, rightOrder.takerAssetAmount),\n \"NEGATIVE_SPREAD_REQUIRED\"\n );\n }\n\n /// @dev Calculates fill amounts for the matched orders.\n /// Each order is filled at their respective price point. However, the calculations are\n /// carried out as though the orders are both being filled at the right order's price point.\n /// The profit made by the leftOrder order goes to the taker (who matched the two orders).\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n /// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.\n /// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.\n /// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.\n function calculateMatchedFillResults(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n uint256 leftOrderTakerAssetFilledAmount,\n uint256 rightOrderTakerAssetFilledAmount\n )\n internal\n pure\n returns (LibFillResults.MatchedFillResults memory matchedFillResults)\n {\n // We settle orders at the exchange rate of the right order.\n // The amount saved by the left maker goes to the taker.\n // Either the left or right order will be fully filled; possibly both.\n // The left order is fully filled iff the right order can sell more than left can buy.\n // That is: the amount required to fill the left order is less than or equal to\n // the amount we can spend from the right order:\n // <= * \n // <= * / \n // * <= * \n uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount);\n uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount);\n uint256 leftTakerAssetFilledAmount;\n uint256 rightTakerAssetFilledAmount;\n if (\n safeMul(leftTakerAssetAmountRemaining, rightOrder.takerAssetAmount) <=\n safeMul(rightTakerAssetAmountRemaining, rightOrder.makerAssetAmount)\n ) {\n // Left order will be fully filled: maximally fill left\n leftTakerAssetFilledAmount = leftTakerAssetAmountRemaining;\n\n // The right order receives an amount proportional to how much was spent.\n rightTakerAssetFilledAmount = getPartialAmount(\n rightOrder.takerAssetAmount,\n rightOrder.makerAssetAmount,\n leftTakerAssetFilledAmount\n );\n } else {\n // Right order will be fully filled: maximally fill right\n rightTakerAssetFilledAmount = rightTakerAssetAmountRemaining;\n\n // The left order receives an amount proportional to how much was spent.\n leftTakerAssetFilledAmount = getPartialAmount(\n rightOrder.makerAssetAmount,\n rightOrder.takerAssetAmount,\n rightTakerAssetFilledAmount\n );\n }\n\n // Calculate fill results for left order\n matchedFillResults.left = calculateFillResults(\n leftOrder,\n leftTakerAssetFilledAmount\n );\n\n // Calculate fill results for right order\n matchedFillResults.right = calculateFillResults(\n rightOrder,\n rightTakerAssetFilledAmount\n );\n\n // Calculate amount given to taker\n matchedFillResults.leftMakerAssetSpreadAmount = safeSub(\n matchedFillResults.left.makerAssetFilledAmount,\n matchedFillResults.right.takerAssetFilledAmount\n );\n\n // Return fill results\n return matchedFillResults;\n }\n\n /// @dev Settles matched order by transferring appropriate funds between order makers, taker, and fee recipient.\n /// @param leftOrder First matched order.\n /// @param rightOrder Second matched order.\n /// @param takerAddress Address that matched the orders. The taker receives the spread between orders as profit.\n /// @param matchedFillResults Struct holding amounts to transfer between makers, taker, and fee recipients.\n function settleMatchedOrders(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n address takerAddress,\n LibFillResults.MatchedFillResults memory matchedFillResults\n )\n private\n {\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n // Order makers and taker\n dispatchTransferFrom(\n leftOrder.makerAssetData,\n leftOrder.makerAddress,\n rightOrder.makerAddress,\n matchedFillResults.right.takerAssetFilledAmount\n );\n dispatchTransferFrom(\n rightOrder.makerAssetData,\n rightOrder.makerAddress,\n leftOrder.makerAddress,\n matchedFillResults.left.takerAssetFilledAmount\n );\n dispatchTransferFrom(\n leftOrder.makerAssetData,\n leftOrder.makerAddress,\n takerAddress,\n matchedFillResults.leftMakerAssetSpreadAmount\n );\n\n // Maker fees\n dispatchTransferFrom(\n zrxAssetData,\n leftOrder.makerAddress,\n leftOrder.feeRecipientAddress,\n matchedFillResults.left.makerFeePaid\n );\n dispatchTransferFrom(\n zrxAssetData,\n rightOrder.makerAddress,\n rightOrder.feeRecipientAddress,\n matchedFillResults.right.makerFeePaid\n );\n\n // Taker fees\n if (leftOrder.feeRecipientAddress == rightOrder.feeRecipientAddress) {\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n leftOrder.feeRecipientAddress,\n safeAdd(\n matchedFillResults.left.takerFeePaid,\n matchedFillResults.right.takerFeePaid\n )\n );\n } else {\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n leftOrder.feeRecipientAddress,\n matchedFillResults.left.takerFeePaid\n );\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n rightOrder.feeRecipientAddress,\n matchedFillResults.right.takerFeePaid\n );\n }\n }\n}\n", - "2.0.0/protocol/Exchange/MixinSignatureValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"./mixins/MSignatureValidator.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./interfaces/IWallet.sol\";\nimport \"./interfaces/IValidator.sol\";\n\n\ncontract MixinSignatureValidator is\n MSignatureValidator,\n MTransactions\n{\n using LibBytes for bytes;\n \n // Mapping of hash => signer => signed\n mapping (bytes32 => mapping (address => bool)) public preSigned;\n\n // Mapping of signer => validator => approved\n mapping (address => mapping (address => bool)) public allowedValidators;\n\n /// @dev Approves a hash on-chain using any valid signature type.\n /// After presigning a hash, the preSign signature type will become valid for that hash and signer.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n function preSign(\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n external\n {\n require(\n isValidSignature(\n hash,\n signerAddress,\n signature\n ),\n \"INVALID_SIGNATURE\"\n );\n preSigned[hash][signerAddress] = true;\n }\n\n /// @dev Approves/unnapproves a Validator contract to verify signatures on signer's behalf.\n /// @param validatorAddress Address of Validator contract.\n /// @param approval Approval or disapproval of Validator contract.\n function setSignatureValidatorApproval(\n address validatorAddress,\n bool approval\n )\n external\n {\n address signerAddress = getCurrentContextAddress();\n allowedValidators[signerAddress][validatorAddress] = approval;\n emit SignatureValidatorApproval(\n signerAddress,\n validatorAddress,\n approval\n );\n }\n\n /// @dev Verifies that a hash has been signed by the given signer.\n /// @param hash Any 32 byte hash.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if the address recovered from the provided signature matches the input signer address.\n function isValidSignature(\n bytes32 hash,\n address signerAddress,\n bytes memory signature\n )\n public\n view\n returns (bool isValid)\n {\n require(\n signature.length > 0,\n \"LENGTH_GREATER_THAN_0_REQUIRED\"\n );\n\n // Pop last byte off of signature byte array.\n uint8 signatureTypeRaw = uint8(signature.popLastByte());\n\n // Ensure signature is supported\n require(\n signatureTypeRaw < uint8(SignatureType.NSignatureTypes),\n \"SIGNATURE_UNSUPPORTED\"\n );\n\n SignatureType signatureType = SignatureType(signatureTypeRaw);\n\n // Variables are not scoped in Solidity.\n uint8 v;\n bytes32 r;\n bytes32 s;\n address recovered;\n\n // Always illegal signature.\n // This is always an implicit option since a signer can create a\n // signature array with invalid type or length. We may as well make\n // it an explicit option. This aids testing and analysis. It is\n // also the initialization value for the enum type.\n if (signatureType == SignatureType.Illegal) {\n revert(\"SIGNATURE_ILLEGAL\");\n\n // Always invalid signature.\n // Like Illegal, this is always implicitly available and therefore\n // offered explicitly. It can be implicitly created by providing\n // a correctly formatted but incorrect signature.\n } else if (signatureType == SignatureType.Invalid) {\n require(\n signature.length == 0,\n \"LENGTH_0_REQUIRED\"\n );\n isValid = false;\n return isValid;\n\n // Signature using EIP712\n } else if (signatureType == SignatureType.EIP712) {\n require(\n signature.length == 65,\n \"LENGTH_65_REQUIRED\"\n );\n v = uint8(signature[0]);\n r = signature.readBytes32(1);\n s = signature.readBytes32(33);\n recovered = ecrecover(\n hash,\n v,\n r,\n s\n );\n isValid = signerAddress == recovered;\n return isValid;\n\n // Signed using web3.eth_sign\n } else if (signatureType == SignatureType.EthSign) {\n require(\n signature.length == 65,\n \"LENGTH_65_REQUIRED\"\n );\n v = uint8(signature[0]);\n r = signature.readBytes32(1);\n s = signature.readBytes32(33);\n recovered = ecrecover(\n keccak256(abi.encodePacked(\n \"\\x19Ethereum Signed Message:\\n32\",\n hash\n )),\n v,\n r,\n s\n );\n isValid = signerAddress == recovered;\n return isValid;\n\n // Implicitly signed by caller.\n // The signer has initiated the call. In the case of non-contract\n // accounts it means the transaction itself was signed.\n // Example: let's say for a particular operation three signatures\n // A, B and C are required. To submit the transaction, A and B can\n // give a signature to C, who can then submit the transaction using\n // `Caller` for his own signature. Or A and C can sign and B can\n // submit using `Caller`. Having `Caller` allows this flexibility.\n } else if (signatureType == SignatureType.Caller) {\n require(\n signature.length == 0,\n \"LENGTH_0_REQUIRED\"\n );\n isValid = signerAddress == msg.sender;\n return isValid;\n\n // Signature verified by wallet contract.\n // If used with an order, the maker of the order is the wallet contract.\n } else if (signatureType == SignatureType.Wallet) {\n isValid = IWallet(signerAddress).isValidSignature(hash, signature);\n return isValid;\n\n // Signature verified by validator contract.\n // If used with an order, the maker of the order can still be an EOA.\n // A signature using this type should be encoded as:\n // | Offset | Length | Contents |\n // | 0x00 | x | Signature to validate |\n // | 0x00 + x | 20 | Address of validator contract |\n // | 0x14 + x | 1 | Signature type is always \"\\x06\" |\n } else if (signatureType == SignatureType.Validator) {\n // Pop last 20 bytes off of signature byte array.\n address validatorAddress = signature.popLast20Bytes();\n \n // Ensure signer has approved validator.\n if (!allowedValidators[signerAddress][validatorAddress]) {\n return false;\n }\n isValid = IValidator(validatorAddress).isValidSignature(\n hash,\n signerAddress,\n signature\n );\n return isValid;\n\n // Signer signed hash previously using the preSign function.\n } else if (signatureType == SignatureType.PreSigned) {\n isValid = preSigned[hash][signerAddress];\n return isValid;\n\n // Signature from Trezor hardware wallet.\n // It differs from web3.eth_sign in the encoding of message length\n // (Bitcoin varint encoding vs ascii-decimal, the latter is not\n // self-terminating which leads to ambiguities).\n // See also:\n // https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer\n // https://github.com/trezor/trezor-mcu/blob/master/firmware/ethereum.c#L602\n // https://github.com/trezor/trezor-mcu/blob/master/firmware/crypto.c#L36\n } else if (signatureType == SignatureType.Trezor) {\n require(\n signature.length == 65,\n \"LENGTH_65_REQUIRED\"\n );\n v = uint8(signature[0]);\n r = signature.readBytes32(1);\n s = signature.readBytes32(33);\n recovered = ecrecover(\n keccak256(abi.encodePacked(\n \"\\x19Ethereum Signed Message:\\n\\x20\",\n hash\n )),\n v,\n r,\n s\n );\n isValid = signerAddress == recovered;\n return isValid;\n }\n\n // Anything else is illegal (We do not return false because\n // the signature may actually be valid, just not in a format\n // that we currently support. In this case returning false\n // may lead the caller to incorrectly believe that the\n // signature was invalid.)\n revert(\"SIGNATURE_UNSUPPORTED\");\n }\n}\n", - "2.0.0/protocol/Exchange/MixinTransactions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\n\nimport \"./libs/LibExchangeErrors.sol\";\nimport \"./mixins/MSignatureValidator.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./libs/LibEIP712.sol\";\n\n\ncontract MixinTransactions is\n LibEIP712,\n MSignatureValidator,\n MTransactions\n{\n\n // Mapping of transaction hash => executed\n // This prevents transactions from being executed more than once.\n mapping (bytes32 => bool) public transactions;\n\n // Address of current transaction signer\n address public currentContextAddress;\n\n // Hash for the EIP712 ZeroEx Transaction Schema\n bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = keccak256(abi.encodePacked(\n \"ZeroExTransaction(\",\n \"uint256 salt,\",\n \"address signerAddress,\",\n \"bytes data\",\n \")\"\n ));\n\n /// @dev Executes an exchange method call in the context of signer.\n /// @param salt Arbitrary number to ensure uniqueness of transaction hash.\n /// @param signerAddress Address of transaction signer.\n /// @param data AbiV2 encoded calldata.\n /// @param signature Proof of signer transaction by signer.\n function executeTransaction(\n uint256 salt,\n address signerAddress,\n bytes data,\n bytes signature\n )\n external\n {\n // Prevent reentrancy\n require(\n currentContextAddress == address(0),\n \"REENTRANCY_ILLEGAL\"\n );\n\n bytes32 transactionHash = hashEIP712Message(hashZeroExTransaction(\n salt,\n signerAddress,\n data\n ));\n\n // Validate transaction has not been executed\n require(\n !transactions[transactionHash],\n \"INVALID_TX_HASH\"\n );\n\n // Transaction always valid if signer is sender of transaction\n if (signerAddress != msg.sender) {\n // Validate signature\n require(\n isValidSignature(\n transactionHash,\n signerAddress,\n signature\n ),\n \"INVALID_TX_SIGNATURE\"\n );\n\n // Set the current transaction signer\n currentContextAddress = signerAddress;\n }\n\n // Execute transaction\n transactions[transactionHash] = true;\n require(\n address(this).delegatecall(data),\n \"FAILED_EXECUTION\"\n );\n\n // Reset current transaction signer if it was previously updated\n if (signerAddress != msg.sender) {\n currentContextAddress = address(0);\n }\n }\n\n /// @dev Calculates EIP712 hash of the Transaction.\n /// @param salt Arbitrary number to ensure uniqueness of transaction hash.\n /// @param signerAddress Address of transaction signer.\n /// @param data AbiV2 encoded calldata.\n /// @return EIP712 hash of the Transaction.\n function hashZeroExTransaction(\n uint256 salt,\n address signerAddress,\n bytes memory data\n )\n internal\n pure\n returns (bytes32 result)\n {\n bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;\n bytes32 dataHash = keccak256(data);\n\n // Assembly for more efficiently computing:\n // keccak256(abi.encodePacked(\n // EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH,\n // salt,\n // bytes32(signerAddress),\n // keccak256(data)\n // ));\n\n assembly {\n // Load free memory pointer\n let memPtr := mload(64)\n\n mstore(memPtr, schemaHash) // hash of schema\n mstore(add(memPtr, 32), salt) // salt\n mstore(add(memPtr, 64), and(signerAddress, 0xffffffffffffffffffffffffffffffffffffffff)) // signerAddress\n mstore(add(memPtr, 96), dataHash) // hash of data\n\n // Compute hash\n result := keccak256(memPtr, 128)\n }\n return result;\n }\n\n /// @dev The current function will be called in the context of this address (either 0x transaction signer or `msg.sender`).\n /// If calling a fill function, this address will represent the taker.\n /// If calling a cancel function, this address will represent the maker.\n /// @return Signer of 0x transaction if entry point is `executeTransaction`.\n /// `msg.sender` if entry point is any other function.\n function getCurrentContextAddress()\n internal\n view\n returns (address)\n {\n address contextAddress = currentContextAddress == address(0) ? msg.sender : currentContextAddress;\n return contextAddress;\n }\n}\n", - "2.0.0/protocol/Exchange/MixinWrapperFunctions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./libs/LibMath.sol\";\nimport \"./libs/LibOrder.sol\";\nimport \"./libs/LibFillResults.sol\";\nimport \"./libs/LibAbiEncoder.sol\";\nimport \"./mixins/MExchangeCore.sol\";\n\n\ncontract MixinWrapperFunctions is\n LibMath,\n LibFillResults,\n LibAbiEncoder,\n MExchangeCore\n{\n\n /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n function fillOrKillOrder(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n returns (FillResults memory fillResults)\n {\n fillResults = fillOrder(\n order,\n takerAssetFillAmount,\n signature\n );\n require(\n fillResults.takerAssetFilledAmount == takerAssetFillAmount,\n \"COMPLETE_FILL_FAILED\"\n );\n return fillResults;\n }\n\n /// @dev Fills the input order.\n /// Returns false if the transaction would otherwise revert.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderNoThrow(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n returns (FillResults memory fillResults)\n {\n // ABI encode calldata for `fillOrder`\n bytes memory fillOrderCalldata = abiEncodeFillOrder(\n order,\n takerAssetFillAmount,\n signature\n );\n\n // Delegate to `fillOrder` and handle any exceptions gracefully\n assembly {\n let success := delegatecall(\n gas, // forward all gas, TODO: look into gas consumption of assert/throw\n address, // call address of this contract\n add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes)\n mload(fillOrderCalldata), // length of input\n fillOrderCalldata, // write output over input\n 128 // output size is 128 bytes\n )\n switch success\n case 0 {\n mstore(fillResults, 0)\n mstore(add(fillResults, 32), 0)\n mstore(add(fillResults, 64), 0)\n mstore(add(fillResults, 96), 0)\n }\n case 1 {\n mstore(fillResults, mload(fillOrderCalldata))\n mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))\n mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))\n mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96)))\n }\n }\n return fillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.\n function batchFillOrders(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n FillResults memory singleFillResults = fillOrder(\n orders[i],\n takerAssetFillAmounts[i],\n signatures[i]\n );\n addFillResults(totalFillResults, singleFillResults);\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrKill.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.\n function batchFillOrKillOrders(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n FillResults memory singleFillResults = fillOrKillOrder(\n orders[i],\n takerAssetFillAmounts[i],\n signatures[i]\n );\n addFillResults(totalFillResults, singleFillResults);\n }\n return totalFillResults;\n }\n\n /// @dev Fills an order with specified parameters and ECDSA signature.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.\n function batchFillOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n takerAssetFillAmounts[i],\n signatures[i]\n );\n addFillResults(totalFillResults, singleFillResults);\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellOrders(\n LibOrder.Order[] memory orders,\n uint256 takerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n bytes memory takerAssetData = orders[0].takerAssetData;\n \n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being sold by taker is the same for each order.\n // Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders.\n orders[i].takerAssetData = takerAssetData;\n\n // Calculate the remaining amount of takerAsset to sell\n uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrder(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of takerAsset has been sold\n if (totalFillResults.takerAssetFilledAmount >= takerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256 takerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n bytes memory takerAssetData = orders[0].takerAssetData;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being sold by taker is the same for each order.\n // Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders.\n orders[i].takerAssetData = takerAssetData;\n\n // Calculate the remaining amount of takerAsset to sell\n uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of takerAsset has been sold\n if (totalFillResults.takerAssetFilledAmount >= takerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of makerAsset is bought by taker.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyOrders(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.\n orders[i].makerAssetData = makerAssetData;\n\n // Calculate the remaining amount of makerAsset to buy\n uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);\n\n // Convert the remaining amount of makerAsset to buy into remaining amount\n // of takerAsset to sell, assuming entire amount can be sold in the current order\n uint256 remainingTakerAssetFillAmount = getPartialAmount(\n orders[i].takerAssetAmount,\n orders[i].makerAssetAmount,\n remainingMakerAssetFillAmount\n );\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrder(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of makerAsset has been bought\n if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.\n orders[i].makerAssetData = makerAssetData;\n\n // Calculate the remaining amount of makerAsset to buy\n uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);\n\n // Convert the remaining amount of makerAsset to buy into remaining amount\n // of takerAsset to sell, assuming entire amount can be sold in the current order\n uint256 remainingTakerAssetFillAmount = getPartialAmount(\n orders[i].takerAssetAmount,\n orders[i].makerAssetAmount,\n remainingMakerAssetFillAmount\n );\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of makerAsset has been bought\n if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously cancels multiple orders in a single transaction.\n /// @param orders Array of order specifications.\n function batchCancelOrders(LibOrder.Order[] memory orders)\n public\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n cancelOrder(orders[i]);\n }\n }\n\n /// @dev Fetches information for all passed in orders.\n /// @param orders Array of order specifications.\n /// @return Array of OrderInfo instances that correspond to each order.\n function getOrdersInfo(LibOrder.Order[] memory orders)\n public\n view\n returns (LibOrder.OrderInfo[] memory)\n {\n uint256 ordersLength = orders.length;\n LibOrder.OrderInfo[] memory ordersInfo = new LibOrder.OrderInfo[](ordersLength);\n for (uint256 i = 0; i != ordersLength; i++) {\n ordersInfo[i] = getOrderInfo(orders[i]);\n }\n return ordersInfo;\n }\n}\n", + "2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/Ownable/Ownable.sol\";\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"./mixins/MAssetProxyDispatcher.sol\";\nimport \"../AssetProxy/interfaces/IAssetProxy.sol\";\n\n\ncontract MixinAssetProxyDispatcher is\n Ownable,\n MAssetProxyDispatcher\n{\n using LibBytes for bytes;\n \n // Mapping from Asset Proxy Id's to their respective Asset Proxy\n mapping (bytes4 => IAssetProxy) public assetProxies;\n\n /// @dev Registers an asset proxy to its asset proxy id.\n /// Once an asset proxy is registered, it cannot be unregistered.\n /// @param assetProxy Address of new asset proxy to register.\n function registerAssetProxy(address assetProxy)\n external\n onlyOwner\n {\n IAssetProxy assetProxyContract = IAssetProxy(assetProxy);\n\n // Ensure that no asset proxy exists with current id.\n bytes4 assetProxyId = assetProxyContract.getProxyId();\n address currentAssetProxy = assetProxies[assetProxyId];\n require(\n currentAssetProxy == address(0),\n \"ASSET_PROXY_ALREADY_EXISTS\"\n );\n\n // Add asset proxy and log registration.\n assetProxies[assetProxyId] = assetProxyContract;\n emit AssetProxyRegistered(\n assetProxyId,\n assetProxy\n );\n }\n\n /// @dev Gets an asset proxy.\n /// @param assetProxyId Id of the asset proxy.\n /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.\n function getAssetProxy(bytes4 assetProxyId)\n external\n view\n returns (address)\n {\n return assetProxies[assetProxyId];\n }\n\n /// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.\n /// @param assetData Byte array encoded for the asset.\n /// @param from Address to transfer token from.\n /// @param to Address to transfer token to.\n /// @param amount Amount of token to transfer.\n function dispatchTransferFrom(\n bytes memory assetData,\n address from,\n address to,\n uint256 amount\n )\n internal\n {\n // Do nothing if no amount should be transferred.\n if (amount > 0 && from != to) {\n // Ensure assetData length is valid\n require(\n assetData.length > 3,\n \"LENGTH_GREATER_THAN_3_REQUIRED\"\n );\n \n // Lookup assetProxy\n bytes4 assetProxyId;\n assembly {\n assetProxyId := and(mload(\n add(assetData, 32)),\n 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000\n )\n }\n address assetProxy = assetProxies[assetProxyId];\n\n // Ensure that assetProxy exists\n require(\n assetProxy != address(0),\n \"ASSET_PROXY_DOES_NOT_EXIST\"\n );\n \n // We construct calldata for the `assetProxy.transferFrom` ABI.\n // The layout of this calldata is in the table below.\n // \n // | Area | Offset | Length | Contents |\n // | -------- |--------|---------|-------------------------------------------- |\n // | Header | 0 | 4 | function selector |\n // | Params | | 4 * 32 | function parameters: |\n // | | 4 | | 1. offset to assetData (*) |\n // | | 36 | | 2. from |\n // | | 68 | | 3. to |\n // | | 100 | | 4. amount |\n // | Data | | | assetData: |\n // | | 132 | 32 | assetData Length |\n // | | 164 | ** | assetData Contents |\n\n assembly {\n /////// Setup State ///////\n // `cdStart` is the start of the calldata for `assetProxy.transferFrom` (equal to free memory ptr).\n let cdStart := mload(64)\n // `dataAreaLength` is the total number of words needed to store `assetData`\n // As-per the ABI spec, this value is padded up to the nearest multiple of 32,\n // and includes 32-bytes for length.\n let dataAreaLength := and(add(mload(assetData), 63), 0xFFFFFFFFFFFE0)\n // `cdEnd` is the end of the calldata for `assetProxy.transferFrom`.\n let cdEnd := add(cdStart, add(132, dataAreaLength))\n\n \n /////// Setup Header Area ///////\n // This area holds the 4-byte `transferFromSelector`.\n // bytes4(keccak256(\"transferFrom(bytes,address,address,uint256)\")) = 0xa85e59e4\n mstore(cdStart, 0xa85e59e400000000000000000000000000000000000000000000000000000000)\n \n /////// Setup Params Area ///////\n // Each parameter is padded to 32-bytes. The entire Params Area is 128 bytes.\n // Notes:\n // 1. The offset to `assetData` is the length of the Params Area (128 bytes).\n // 2. A 20-byte mask is applied to addresses to zero-out the unused bytes.\n mstore(add(cdStart, 4), 128)\n mstore(add(cdStart, 36), and(from, 0xffffffffffffffffffffffffffffffffffffffff))\n mstore(add(cdStart, 68), and(to, 0xffffffffffffffffffffffffffffffffffffffff))\n mstore(add(cdStart, 100), amount)\n \n /////// Setup Data Area ///////\n // This area holds `assetData`.\n let dataArea := add(cdStart, 132)\n // solhint-disable-next-line no-empty-blocks\n for {} lt(dataArea, cdEnd) {} {\n mstore(dataArea, mload(assetData))\n dataArea := add(dataArea, 32)\n assetData := add(assetData, 32)\n }\n\n /////// Call `assetProxy.transferFrom` using the constructed calldata ///////\n let success := call(\n gas, // forward all gas\n assetProxy, // call address of asset proxy\n 0, // don't send any ETH\n cdStart, // pointer to start of input\n sub(cdEnd, cdStart), // length of input \n cdStart, // write output over input\n 512 // reserve 512 bytes for output\n )\n if iszero(success) {\n revert(cdStart, returndatasize())\n }\n }\n }\n }\n}\n", + "2.0.0/protocol/Exchange/MixinExchangeCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../utils/ReentrancyGuard/ReentrancyGuard.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./libs/LibFillResults.sol\";\nimport \"./libs/LibOrder.sol\";\nimport \"./libs/LibMath.sol\";\nimport \"./mixins/MExchangeCore.sol\";\nimport \"./mixins/MSignatureValidator.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./mixins/MAssetProxyDispatcher.sol\";\n\n\ncontract MixinExchangeCore is\n ReentrancyGuard,\n LibConstants,\n LibMath,\n LibOrder,\n LibFillResults,\n MAssetProxyDispatcher,\n MExchangeCore,\n MSignatureValidator,\n MTransactions\n{\n // Mapping of orderHash => amount of takerAsset already bought by maker\n mapping (bytes32 => uint256) public filled;\n\n // Mapping of orderHash => cancelled\n mapping (bytes32 => bool) public cancelled;\n\n // Mapping of makerAddress => senderAddress => lowest salt an order can have in order to be fillable\n // Orders with specified senderAddress and with a salt less than their epoch are considered cancelled\n mapping (address => mapping (address => uint256)) public orderEpoch;\n\n /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch\n /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress).\n /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.\n function cancelOrdersUpTo(uint256 targetOrderEpoch)\n external\n nonReentrant\n {\n address makerAddress = getCurrentContextAddress();\n // If this function is called via `executeTransaction`, we only update the orderEpoch for the makerAddress/msg.sender combination.\n // This allows external filter contracts to add rules to how orders are cancelled via this function.\n address senderAddress = makerAddress == msg.sender ? address(0) : msg.sender;\n\n // orderEpoch is initialized to 0, so to cancelUpTo we need salt + 1\n uint256 newOrderEpoch = targetOrderEpoch + 1; \n uint256 oldOrderEpoch = orderEpoch[makerAddress][senderAddress];\n\n // Ensure orderEpoch is monotonically increasing\n require(\n newOrderEpoch > oldOrderEpoch, \n \"INVALID_NEW_ORDER_EPOCH\"\n );\n\n // Update orderEpoch\n orderEpoch[makerAddress][senderAddress] = newOrderEpoch;\n emit CancelUpTo(makerAddress, senderAddress, newOrderEpoch);\n }\n\n /// @dev Fills the input order.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrder(\n Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n nonReentrant\n returns (FillResults memory fillResults)\n {\n fillResults = fillOrderInternal(\n order,\n takerAssetFillAmount,\n signature\n );\n return fillResults;\n }\n\n /// @dev After calling, the order can not be filled anymore.\n /// Throws if order is invalid or sender does not have permission to cancel.\n /// @param order Order to cancel. Order must be OrderStatus.FILLABLE.\n function cancelOrder(Order memory order)\n public\n nonReentrant\n {\n // Fetch current order status\n OrderInfo memory orderInfo = getOrderInfo(order);\n\n // Validate context\n assertValidCancel(order, orderInfo);\n\n // Perform cancel\n updateCancelledState(order, orderInfo.orderHash);\n }\n\n /// @dev Gets information about an order: status, hash, and amount filled.\n /// @param order Order to gather information on.\n /// @return OrderInfo Information about the order and its state.\n /// See LibOrder.OrderInfo for a complete description.\n function getOrderInfo(Order memory order)\n public\n view\n returns (OrderInfo memory orderInfo)\n {\n // Compute the order hash\n orderInfo.orderHash = getOrderHash(order);\n\n // Fetch filled amount\n orderInfo.orderTakerAssetFilledAmount = filled[orderInfo.orderHash];\n\n // If order.makerAssetAmount is zero, we also reject the order.\n // While the Exchange contract handles them correctly, they create\n // edge cases in the supporting infrastructure because they have\n // an 'infinite' price when computed by a simple division.\n if (order.makerAssetAmount == 0) {\n orderInfo.orderStatus = uint8(OrderStatus.INVALID_MAKER_ASSET_AMOUNT);\n return orderInfo;\n }\n\n // If order.takerAssetAmount is zero, then the order will always\n // be considered filled because 0 == takerAssetAmount == orderTakerAssetFilledAmount\n // Instead of distinguishing between unfilled and filled zero taker\n // amount orders, we choose not to support them.\n if (order.takerAssetAmount == 0) {\n orderInfo.orderStatus = uint8(OrderStatus.INVALID_TAKER_ASSET_AMOUNT);\n return orderInfo;\n }\n\n // Validate order availability\n if (orderInfo.orderTakerAssetFilledAmount >= order.takerAssetAmount) {\n orderInfo.orderStatus = uint8(OrderStatus.FULLY_FILLED);\n return orderInfo;\n }\n\n // Validate order expiration\n // solhint-disable-next-line not-rely-on-time\n if (block.timestamp >= order.expirationTimeSeconds) {\n orderInfo.orderStatus = uint8(OrderStatus.EXPIRED);\n return orderInfo;\n }\n\n // Check if order has been cancelled\n if (cancelled[orderInfo.orderHash]) {\n orderInfo.orderStatus = uint8(OrderStatus.CANCELLED);\n return orderInfo;\n }\n if (orderEpoch[order.makerAddress][order.senderAddress] > order.salt) {\n orderInfo.orderStatus = uint8(OrderStatus.CANCELLED);\n return orderInfo;\n }\n\n // All other statuses are ruled out: order is Fillable\n orderInfo.orderStatus = uint8(OrderStatus.FILLABLE);\n return orderInfo;\n }\n\n /// @dev Fills the input order.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderInternal(\n Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (FillResults memory fillResults)\n {\n // Fetch order info\n OrderInfo memory orderInfo = getOrderInfo(order);\n\n // Fetch taker address\n address takerAddress = getCurrentContextAddress();\n \n // Assert that the order is fillable by taker\n assertFillableOrder(\n order,\n orderInfo,\n takerAddress,\n signature\n );\n \n // Get amount of takerAsset to fill\n uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount);\n uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount);\n\n // Validate context\n assertValidFill(\n order,\n orderInfo,\n takerAssetFillAmount,\n takerAssetFilledAmount,\n fillResults.makerAssetFilledAmount\n );\n\n // Compute proportional fill amounts\n fillResults = calculateFillResults(order, takerAssetFilledAmount);\n\n // Update exchange internal state\n updateFilledState(\n order,\n takerAddress,\n orderInfo.orderHash,\n orderInfo.orderTakerAssetFilledAmount,\n fillResults\n );\n \n // Settle order\n settleOrder(order, takerAddress, fillResults);\n\n return fillResults;\n }\n\n /// @dev Updates state with results of a fill order.\n /// @param order that was filled.\n /// @param takerAddress Address of taker who filled the order.\n /// @param orderTakerAssetFilledAmount Amount of order already filled.\n function updateFilledState(\n Order memory order,\n address takerAddress,\n bytes32 orderHash,\n uint256 orderTakerAssetFilledAmount,\n FillResults memory fillResults\n )\n internal\n {\n // Update state\n filled[orderHash] = safeAdd(orderTakerAssetFilledAmount, fillResults.takerAssetFilledAmount);\n\n // Log order\n emit Fill(\n order.makerAddress,\n order.feeRecipientAddress,\n takerAddress,\n msg.sender,\n fillResults.makerAssetFilledAmount,\n fillResults.takerAssetFilledAmount,\n fillResults.makerFeePaid,\n fillResults.takerFeePaid,\n orderHash,\n order.makerAssetData,\n order.takerAssetData\n );\n }\n\n /// @dev Updates state with results of cancelling an order.\n /// State is only updated if the order is currently fillable.\n /// Otherwise, updating state would have no effect.\n /// @param order that was cancelled.\n /// @param orderHash Hash of order that was cancelled.\n function updateCancelledState(\n Order memory order,\n bytes32 orderHash\n )\n internal\n {\n // Perform cancel\n cancelled[orderHash] = true;\n\n // Log cancel\n emit Cancel(\n order.makerAddress,\n order.feeRecipientAddress,\n msg.sender,\n orderHash,\n order.makerAssetData,\n order.takerAssetData\n );\n }\n \n /// @dev Validates context for fillOrder. Succeeds or throws.\n /// @param order to be filled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n /// @param takerAddress Address of order taker.\n /// @param signature Proof that the orders was created by its maker.\n function assertFillableOrder(\n Order memory order,\n OrderInfo memory orderInfo,\n address takerAddress,\n bytes memory signature\n )\n internal\n view\n {\n // An order can only be filled if its status is FILLABLE.\n require(\n orderInfo.orderStatus == uint8(OrderStatus.FILLABLE),\n \"ORDER_UNFILLABLE\"\n );\n \n // Validate sender is allowed to fill this order\n if (order.senderAddress != address(0)) {\n require(\n order.senderAddress == msg.sender,\n \"INVALID_SENDER\"\n );\n }\n \n // Validate taker is allowed to fill this order\n if (order.takerAddress != address(0)) {\n require(\n order.takerAddress == takerAddress,\n \"INVALID_TAKER\"\n );\n }\n \n // Validate Maker signature (check only if first time seen)\n if (orderInfo.orderTakerAssetFilledAmount == 0) {\n require(\n isValidSignature(\n orderInfo.orderHash,\n order.makerAddress,\n signature\n ),\n \"INVALID_ORDER_SIGNATURE\"\n );\n }\n }\n \n /// @dev Validates context for fillOrder. Succeeds or throws.\n /// @param order to be filled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n /// @param takerAssetFillAmount Desired amount of order to fill by taker.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered.\n function assertValidFill(\n Order memory order,\n OrderInfo memory orderInfo,\n uint256 takerAssetFillAmount, // TODO: use FillResults\n uint256 takerAssetFilledAmount,\n uint256 makerAssetFilledAmount\n )\n internal\n view\n {\n // Revert if fill amount is invalid\n // TODO: reconsider necessity for v2.1\n require(\n takerAssetFillAmount != 0,\n \"INVALID_TAKER_AMOUNT\"\n );\n \n // Make sure taker does not pay more than desired amount\n // NOTE: This assertion should never fail, it is here\n // as an extra defence against potential bugs.\n require(\n takerAssetFilledAmount <= takerAssetFillAmount,\n \"TAKER_OVERPAY\"\n );\n \n // Make sure order is not overfilled\n // NOTE: This assertion should never fail, it is here\n // as an extra defence against potential bugs.\n require(\n safeAdd(orderInfo.orderTakerAssetFilledAmount, takerAssetFilledAmount) <= order.takerAssetAmount,\n \"ORDER_OVERFILL\"\n );\n \n // Make sure order is filled at acceptable price.\n // The order has an implied price from the makers perspective:\n // order price = order.makerAssetAmount / order.takerAssetAmount\n // i.e. the number of makerAsset maker is paying per takerAsset. The\n // maker is guaranteed to get this price or a better (lower) one. The\n // actual price maker is getting in this fill is:\n // fill price = makerAssetFilledAmount / takerAssetFilledAmount\n // We need `fill price <= order price` for the fill to be fair to maker.\n // This amounts to:\n // makerAssetFilledAmount order.makerAssetAmount\n // ------------------------ <= -----------------------\n // takerAssetFilledAmount order.takerAssetAmount\n // or, equivalently:\n // makerAssetFilledAmount * order.takerAssetAmount <=\n // order.makerAssetAmount * takerAssetFilledAmount\n // NOTE: This assertion should never fail, it is here\n // as an extra defence against potential bugs.\n require(\n safeMul(makerAssetFilledAmount, order.takerAssetAmount)\n <= \n safeMul(order.makerAssetAmount, takerAssetFilledAmount),\n \"INVALID_FILL_PRICE\"\n );\n \n // Validate fill order rounding\n require(\n !isRoundingErrorFloor(\n takerAssetFilledAmount,\n order.takerAssetAmount,\n order.makerAssetAmount\n ),\n \"ROUNDING_ERROR\"\n );\n }\n\n /// @dev Validates context for cancelOrder. Succeeds or throws.\n /// @param order to be cancelled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n function assertValidCancel(\n Order memory order,\n OrderInfo memory orderInfo\n )\n internal\n view\n {\n // Ensure order is valid\n // An order can only be cancelled if its status is FILLABLE.\n require(\n orderInfo.orderStatus == uint8(OrderStatus.FILLABLE),\n \"ORDER_UNFILLABLE\"\n );\n\n // Validate sender is allowed to cancel this order\n if (order.senderAddress != address(0)) {\n require(\n order.senderAddress == msg.sender,\n \"INVALID_SENDER\"\n );\n }\n\n // Validate transaction signed by maker\n address makerAddress = getCurrentContextAddress();\n require(\n order.makerAddress == makerAddress,\n \"INVALID_MAKER\"\n );\n }\n\n /// @dev Calculates amounts filled and fees paid by maker and taker.\n /// @param order to be filled.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @return fillResults Amounts filled and fees paid by maker and taker.\n function calculateFillResults(\n Order memory order,\n uint256 takerAssetFilledAmount\n )\n internal\n pure\n returns (FillResults memory fillResults)\n {\n // Compute proportional transfer amounts\n fillResults.takerAssetFilledAmount = takerAssetFilledAmount;\n fillResults.makerAssetFilledAmount = getPartialAmountFloor(\n takerAssetFilledAmount,\n order.takerAssetAmount,\n order.makerAssetAmount\n );\n fillResults.makerFeePaid = getPartialAmountFloor(\n takerAssetFilledAmount,\n order.takerAssetAmount,\n order.makerFee\n );\n fillResults.takerFeePaid = getPartialAmountFloor(\n takerAssetFilledAmount,\n order.takerAssetAmount,\n order.takerFee\n );\n\n return fillResults;\n }\n\n /// @dev Settles an order by transferring assets between counterparties.\n /// @param order Order struct containing order specifications.\n /// @param takerAddress Address selling takerAsset and buying makerAsset.\n /// @param fillResults Amounts to be filled and fees paid by maker and taker.\n function settleOrder(\n LibOrder.Order memory order,\n address takerAddress,\n LibFillResults.FillResults memory fillResults\n )\n private\n {\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n dispatchTransferFrom(\n order.makerAssetData,\n order.makerAddress,\n takerAddress,\n fillResults.makerAssetFilledAmount\n );\n dispatchTransferFrom(\n order.takerAssetData,\n takerAddress,\n order.makerAddress,\n fillResults.takerAssetFilledAmount\n );\n dispatchTransferFrom(\n zrxAssetData,\n order.makerAddress,\n order.feeRecipientAddress,\n fillResults.makerFeePaid\n );\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n order.feeRecipientAddress,\n fillResults.takerFeePaid\n );\n }\n}\n", + "2.0.0/protocol/Exchange/MixinMatchOrders.sol": "/*\n Copyright 2018 ZeroEx Intl.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n http://www.apache.org/licenses/LICENSE-2.0\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../utils/ReentrancyGuard/ReentrancyGuard.sol\";\nimport \"./libs/LibConstants.sol\";\nimport \"./libs/LibMath.sol\";\nimport \"./libs/LibOrder.sol\";\nimport \"./libs/LibFillResults.sol\";\nimport \"./mixins/MExchangeCore.sol\";\nimport \"./mixins/MMatchOrders.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./mixins/MAssetProxyDispatcher.sol\";\n\n\ncontract MixinMatchOrders is\n ReentrancyGuard,\n LibConstants,\n LibMath,\n MAssetProxyDispatcher,\n MExchangeCore,\n MMatchOrders,\n MTransactions\n{\n /// @dev Match two complementary orders that have a profitable spread.\n /// Each order is filled at their respective price point. However, the calculations are\n /// carried out as though the orders are both being filled at the right order's price point.\n /// The profit made by the left order goes to the taker (who matched the two orders).\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n /// @param leftSignature Proof that order was created by the left maker.\n /// @param rightSignature Proof that order was created by the right maker.\n /// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders.\n function matchOrders(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n bytes memory leftSignature,\n bytes memory rightSignature\n )\n public\n nonReentrant\n returns (LibFillResults.MatchedFillResults memory matchedFillResults)\n {\n // We assume that rightOrder.takerAssetData == leftOrder.makerAssetData and rightOrder.makerAssetData == leftOrder.takerAssetData.\n // If this assumption isn't true, the match will fail at signature validation.\n rightOrder.makerAssetData = leftOrder.takerAssetData;\n rightOrder.takerAssetData = leftOrder.makerAssetData;\n\n // Get left & right order info\n LibOrder.OrderInfo memory leftOrderInfo = getOrderInfo(leftOrder);\n LibOrder.OrderInfo memory rightOrderInfo = getOrderInfo(rightOrder);\n\n // Fetch taker address\n address takerAddress = getCurrentContextAddress();\n \n // Either our context is valid or we revert\n assertFillableOrder(\n leftOrder,\n leftOrderInfo,\n takerAddress,\n leftSignature\n );\n assertFillableOrder(\n rightOrder,\n rightOrderInfo,\n takerAddress,\n rightSignature\n );\n assertValidMatch(leftOrder, rightOrder);\n\n // Compute proportional fill amounts\n matchedFillResults = calculateMatchedFillResults(\n leftOrder,\n rightOrder,\n leftOrderInfo.orderTakerAssetFilledAmount,\n rightOrderInfo.orderTakerAssetFilledAmount\n );\n\n // Validate fill contexts\n assertValidFill(\n leftOrder,\n leftOrderInfo,\n matchedFillResults.left.takerAssetFilledAmount,\n matchedFillResults.left.takerAssetFilledAmount,\n matchedFillResults.left.makerAssetFilledAmount\n );\n assertValidFill(\n rightOrder,\n rightOrderInfo,\n matchedFillResults.right.takerAssetFilledAmount,\n matchedFillResults.right.takerAssetFilledAmount,\n matchedFillResults.right.makerAssetFilledAmount\n );\n \n // Update exchange state\n updateFilledState(\n leftOrder,\n takerAddress,\n leftOrderInfo.orderHash,\n leftOrderInfo.orderTakerAssetFilledAmount,\n matchedFillResults.left\n );\n updateFilledState(\n rightOrder,\n takerAddress,\n rightOrderInfo.orderHash,\n rightOrderInfo.orderTakerAssetFilledAmount,\n matchedFillResults.right\n );\n\n // Settle matched orders. Succeeds or throws.\n settleMatchedOrders(\n leftOrder,\n rightOrder,\n takerAddress,\n matchedFillResults\n );\n\n return matchedFillResults;\n }\n\n /// @dev Validates context for matchOrders. Succeeds or throws.\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n function assertValidMatch(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder\n )\n internal\n pure\n {\n // Make sure there is a profitable spread.\n // There is a profitable spread iff the cost per unit bought (OrderA.MakerAmount/OrderA.TakerAmount) for each order is greater\n // than the profit per unit sold of the matched order (OrderB.TakerAmount/OrderB.MakerAmount).\n // This is satisfied by the equations below:\n // / >= / \n // AND\n // / >= / \n // These equations can be combined to get the following:\n require(\n safeMul(leftOrder.makerAssetAmount, rightOrder.makerAssetAmount) >=\n safeMul(leftOrder.takerAssetAmount, rightOrder.takerAssetAmount),\n \"NEGATIVE_SPREAD_REQUIRED\"\n );\n }\n\n /// @dev Calculates fill amounts for the matched orders.\n /// Each order is filled at their respective price point. However, the calculations are\n /// carried out as though the orders are both being filled at the right order's price point.\n /// The profit made by the leftOrder order goes to the taker (who matched the two orders).\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n /// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.\n /// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.\n /// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.\n function calculateMatchedFillResults(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n uint256 leftOrderTakerAssetFilledAmount,\n uint256 rightOrderTakerAssetFilledAmount\n )\n internal\n pure\n returns (LibFillResults.MatchedFillResults memory matchedFillResults)\n {\n // Derive maker asset amounts for left & right orders, given store taker assert amounts\n uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount);\n uint256 leftMakerAssetAmountRemaining = getPartialAmountFloor(\n leftOrder.makerAssetAmount,\n leftOrder.takerAssetAmount,\n leftTakerAssetAmountRemaining\n );\n uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount);\n uint256 rightMakerAssetAmountRemaining = getPartialAmountFloor(\n rightOrder.makerAssetAmount,\n rightOrder.takerAssetAmount,\n rightTakerAssetAmountRemaining\n );\n\n // Calculate fill results for maker and taker assets: at least one order will be fully filled.\n // The maximum amount the left maker can buy is `leftTakerAssetAmountRemaining`\n // The maximum amount the right maker can sell is `rightMakerAssetAmountRemaining`\n // We have two distinct cases for calculating the fill results:\n // Case 1.\n // If the left maker can buy more than the right maker can sell, then only the right order is fully filled.\n // If the left maker can buy exactly what the right maker can sell, then both orders are fully filled.\n // Case 2.\n // If the left maker cannot buy more than the right maker can sell, then only the left order is fully filled.\n if (leftTakerAssetAmountRemaining >= rightMakerAssetAmountRemaining) {\n // Case 1: Right order is fully filled\n matchedFillResults.right.makerAssetFilledAmount = rightMakerAssetAmountRemaining;\n matchedFillResults.right.takerAssetFilledAmount = rightTakerAssetAmountRemaining;\n matchedFillResults.left.takerAssetFilledAmount = matchedFillResults.right.makerAssetFilledAmount;\n // Round down to ensure the maker's exchange rate does not exceed the price specified by the order. \n // We favor the maker when the exchange rate must be rounded.\n matchedFillResults.left.makerAssetFilledAmount = getPartialAmountFloor(\n leftOrder.makerAssetAmount,\n leftOrder.takerAssetAmount,\n matchedFillResults.left.takerAssetFilledAmount\n );\n } else {\n // Case 2: Left order is fully filled\n matchedFillResults.left.makerAssetFilledAmount = leftMakerAssetAmountRemaining;\n matchedFillResults.left.takerAssetFilledAmount = leftTakerAssetAmountRemaining;\n matchedFillResults.right.makerAssetFilledAmount = matchedFillResults.left.takerAssetFilledAmount;\n // Round up to ensure the maker's exchange rate does not exceed the price specified by the order.\n // We favor the maker when the exchange rate must be rounded.\n matchedFillResults.right.takerAssetFilledAmount = getPartialAmountCeil(\n rightOrder.takerAssetAmount,\n rightOrder.makerAssetAmount,\n matchedFillResults.right.makerAssetFilledAmount\n );\n }\n\n // Calculate amount given to taker\n matchedFillResults.leftMakerAssetSpreadAmount = safeSub(\n matchedFillResults.left.makerAssetFilledAmount,\n matchedFillResults.right.takerAssetFilledAmount\n );\n\n // Compute fees for left order\n matchedFillResults.left.makerFeePaid = getPartialAmountFloor(\n matchedFillResults.left.makerAssetFilledAmount,\n leftOrder.makerAssetAmount,\n leftOrder.makerFee\n );\n matchedFillResults.left.takerFeePaid = getPartialAmountFloor(\n matchedFillResults.left.takerAssetFilledAmount,\n leftOrder.takerAssetAmount,\n leftOrder.takerFee\n );\n\n // Compute fees for right order\n matchedFillResults.right.makerFeePaid = getPartialAmountFloor(\n matchedFillResults.right.makerAssetFilledAmount,\n rightOrder.makerAssetAmount,\n rightOrder.makerFee\n );\n matchedFillResults.right.takerFeePaid = getPartialAmountFloor(\n matchedFillResults.right.takerAssetFilledAmount,\n rightOrder.takerAssetAmount,\n rightOrder.takerFee\n );\n\n // Return fill results\n return matchedFillResults;\n }\n\n /// @dev Settles matched order by transferring appropriate funds between order makers, taker, and fee recipient.\n /// @param leftOrder First matched order.\n /// @param rightOrder Second matched order.\n /// @param takerAddress Address that matched the orders. The taker receives the spread between orders as profit.\n /// @param matchedFillResults Struct holding amounts to transfer between makers, taker, and fee recipients.\n function settleMatchedOrders(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n address takerAddress,\n LibFillResults.MatchedFillResults memory matchedFillResults\n )\n private\n {\n bytes memory zrxAssetData = ZRX_ASSET_DATA;\n // Order makers and taker\n dispatchTransferFrom(\n leftOrder.makerAssetData,\n leftOrder.makerAddress,\n rightOrder.makerAddress,\n matchedFillResults.right.takerAssetFilledAmount\n );\n dispatchTransferFrom(\n rightOrder.makerAssetData,\n rightOrder.makerAddress,\n leftOrder.makerAddress,\n matchedFillResults.left.takerAssetFilledAmount\n );\n dispatchTransferFrom(\n leftOrder.makerAssetData,\n leftOrder.makerAddress,\n takerAddress,\n matchedFillResults.leftMakerAssetSpreadAmount\n );\n\n // Maker fees\n dispatchTransferFrom(\n zrxAssetData,\n leftOrder.makerAddress,\n leftOrder.feeRecipientAddress,\n matchedFillResults.left.makerFeePaid\n );\n dispatchTransferFrom(\n zrxAssetData,\n rightOrder.makerAddress,\n rightOrder.feeRecipientAddress,\n matchedFillResults.right.makerFeePaid\n );\n\n // Taker fees\n if (leftOrder.feeRecipientAddress == rightOrder.feeRecipientAddress) {\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n leftOrder.feeRecipientAddress,\n safeAdd(\n matchedFillResults.left.takerFeePaid,\n matchedFillResults.right.takerFeePaid\n )\n );\n } else {\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n leftOrder.feeRecipientAddress,\n matchedFillResults.left.takerFeePaid\n );\n dispatchTransferFrom(\n zrxAssetData,\n takerAddress,\n rightOrder.feeRecipientAddress,\n matchedFillResults.right.takerFeePaid\n );\n }\n }\n}\n", + "2.0.0/protocol/Exchange/MixinSignatureValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../utils/LibBytes/LibBytes.sol\";\nimport \"../../utils/ReentrancyGuard/ReentrancyGuard.sol\";\nimport \"./mixins/MSignatureValidator.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./interfaces/IWallet.sol\";\nimport \"./interfaces/IValidator.sol\";\n\n\ncontract MixinSignatureValidator is\n ReentrancyGuard,\n MSignatureValidator,\n MTransactions\n{\n using LibBytes for bytes;\n \n // Mapping of hash => signer => signed\n mapping (bytes32 => mapping (address => bool)) public preSigned;\n\n // Mapping of signer => validator => approved\n mapping (address => mapping (address => bool)) public allowedValidators;\n\n /// @dev Approves a hash on-chain using any valid signature type.\n /// After presigning a hash, the preSign signature type will become valid for that hash and signer.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n function preSign(\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n external\n {\n if (signerAddress != msg.sender) {\n require(\n isValidSignature(\n hash,\n signerAddress,\n signature\n ),\n \"INVALID_SIGNATURE\"\n );\n }\n preSigned[hash][signerAddress] = true;\n }\n\n /// @dev Approves/unnapproves a Validator contract to verify signatures on signer's behalf.\n /// @param validatorAddress Address of Validator contract.\n /// @param approval Approval or disapproval of Validator contract.\n function setSignatureValidatorApproval(\n address validatorAddress,\n bool approval\n )\n external\n nonReentrant\n {\n address signerAddress = getCurrentContextAddress();\n allowedValidators[signerAddress][validatorAddress] = approval;\n emit SignatureValidatorApproval(\n signerAddress,\n validatorAddress,\n approval\n );\n }\n\n /// @dev Verifies that a hash has been signed by the given signer.\n /// @param hash Any 32 byte hash.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if the address recovered from the provided signature matches the input signer address.\n function isValidSignature(\n bytes32 hash,\n address signerAddress,\n bytes memory signature\n )\n public\n view\n returns (bool isValid)\n {\n require(\n signature.length > 0,\n \"LENGTH_GREATER_THAN_0_REQUIRED\"\n );\n\n // Pop last byte off of signature byte array.\n uint8 signatureTypeRaw = uint8(signature.popLastByte());\n\n // Ensure signature is supported\n require(\n signatureTypeRaw < uint8(SignatureType.NSignatureTypes),\n \"SIGNATURE_UNSUPPORTED\"\n );\n\n SignatureType signatureType = SignatureType(signatureTypeRaw);\n\n // Variables are not scoped in Solidity.\n uint8 v;\n bytes32 r;\n bytes32 s;\n address recovered;\n\n // Always illegal signature.\n // This is always an implicit option since a signer can create a\n // signature array with invalid type or length. We may as well make\n // it an explicit option. This aids testing and analysis. It is\n // also the initialization value for the enum type.\n if (signatureType == SignatureType.Illegal) {\n revert(\"SIGNATURE_ILLEGAL\");\n\n // Always invalid signature.\n // Like Illegal, this is always implicitly available and therefore\n // offered explicitly. It can be implicitly created by providing\n // a correctly formatted but incorrect signature.\n } else if (signatureType == SignatureType.Invalid) {\n require(\n signature.length == 0,\n \"LENGTH_0_REQUIRED\"\n );\n isValid = false;\n return isValid;\n\n // Signature using EIP712\n } else if (signatureType == SignatureType.EIP712) {\n require(\n signature.length == 65,\n \"LENGTH_65_REQUIRED\"\n );\n v = uint8(signature[0]);\n r = signature.readBytes32(1);\n s = signature.readBytes32(33);\n recovered = ecrecover(\n hash,\n v,\n r,\n s\n );\n isValid = signerAddress == recovered;\n return isValid;\n\n // Signed using web3.eth_sign\n } else if (signatureType == SignatureType.EthSign) {\n require(\n signature.length == 65,\n \"LENGTH_65_REQUIRED\"\n );\n v = uint8(signature[0]);\n r = signature.readBytes32(1);\n s = signature.readBytes32(33);\n recovered = ecrecover(\n keccak256(abi.encodePacked(\n \"\\x19Ethereum Signed Message:\\n32\",\n hash\n )),\n v,\n r,\n s\n );\n isValid = signerAddress == recovered;\n return isValid;\n\n // Signature verified by wallet contract.\n // If used with an order, the maker of the order is the wallet contract.\n } else if (signatureType == SignatureType.Wallet) {\n isValid = isValidWalletSignature(\n hash,\n signerAddress,\n signature\n );\n return isValid;\n\n // Signature verified by validator contract.\n // If used with an order, the maker of the order can still be an EOA.\n // A signature using this type should be encoded as:\n // | Offset | Length | Contents |\n // | 0x00 | x | Signature to validate |\n // | 0x00 + x | 20 | Address of validator contract |\n // | 0x14 + x | 1 | Signature type is always \"\\x06\" |\n } else if (signatureType == SignatureType.Validator) {\n // Pop last 20 bytes off of signature byte array.\n address validatorAddress = signature.popLast20Bytes();\n \n // Ensure signer has approved validator.\n if (!allowedValidators[signerAddress][validatorAddress]) {\n return false;\n }\n isValid = isValidValidatorSignature(\n validatorAddress,\n hash,\n signerAddress,\n signature\n );\n return isValid;\n\n // Signer signed hash previously using the preSign function.\n } else if (signatureType == SignatureType.PreSigned) {\n isValid = preSigned[hash][signerAddress];\n return isValid;\n }\n\n // Anything else is illegal (We do not return false because\n // the signature may actually be valid, just not in a format\n // that we currently support. In this case returning false\n // may lead the caller to incorrectly believe that the\n // signature was invalid.)\n revert(\"SIGNATURE_UNSUPPORTED\");\n }\n\n /// @dev Verifies signature using logic defined by Wallet contract.\n /// @param hash Any 32 byte hash.\n /// @param walletAddress Address that should have signed the given hash\n /// and defines its own signature verification method.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if signature is valid for given wallet..\n function isValidWalletSignature(\n bytes32 hash,\n address walletAddress,\n bytes signature\n )\n internal\n view\n returns (bool isValid)\n {\n bytes memory calldata = abi.encodeWithSelector(\n IWallet(walletAddress).isValidSignature.selector,\n hash,\n signature\n );\n assembly {\n let cdStart := add(calldata, 32)\n let success := staticcall(\n gas, // forward all gas\n walletAddress, // address of Wallet contract\n cdStart, // pointer to start of input\n mload(calldata), // length of input\n cdStart, // write input over output\n 32 // output size is 32 bytes\n )\n\n switch success\n case 0 {\n // Revert with `Error(\"WALLET_ERROR\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000c57414c4c45545f4552524f5200000000000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n case 1 {\n // Signature is valid if call did not revert and returned true\n isValid := mload(cdStart)\n }\n }\n return isValid;\n }\n\n /// @dev Verifies signature using logic defined by Validator contract.\n /// @param validatorAddress Address of validator contract.\n /// @param hash Any 32 byte hash.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if the address recovered from the provided signature matches the input signer address.\n function isValidValidatorSignature(\n address validatorAddress,\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n internal\n view\n returns (bool isValid)\n {\n bytes memory calldata = abi.encodeWithSelector(\n IValidator(signerAddress).isValidSignature.selector,\n hash,\n signerAddress,\n signature\n );\n assembly {\n let cdStart := add(calldata, 32)\n let success := staticcall(\n gas, // forward all gas\n validatorAddress, // address of Validator contract\n cdStart, // pointer to start of input\n mload(calldata), // length of input\n cdStart, // write input over output\n 32 // output size is 32 bytes\n )\n\n switch success\n case 0 {\n // Revert with `Error(\"VALIDATOR_ERROR\")`\n mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)\n mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)\n mstore(64, 0x0000000f56414c494441544f525f4552524f5200000000000000000000000000)\n mstore(96, 0)\n revert(0, 100)\n }\n case 1 {\n // Signature is valid if call did not revert and returned true\n isValid := mload(cdStart)\n }\n }\n return isValid;\n }\n}\n", + "2.0.0/protocol/Exchange/MixinTransactions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\n\nimport \"./libs/LibExchangeErrors.sol\";\nimport \"./mixins/MSignatureValidator.sol\";\nimport \"./mixins/MTransactions.sol\";\nimport \"./libs/LibEIP712.sol\";\n\n\ncontract MixinTransactions is\n LibEIP712,\n MSignatureValidator,\n MTransactions\n{\n\n // Mapping of transaction hash => executed\n // This prevents transactions from being executed more than once.\n mapping (bytes32 => bool) public transactions;\n\n // Address of current transaction signer\n address public currentContextAddress;\n\n // Hash for the EIP712 ZeroEx Transaction Schema\n bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = keccak256(abi.encodePacked(\n \"ZeroExTransaction(\",\n \"uint256 salt,\",\n \"address signerAddress,\",\n \"bytes data\",\n \")\"\n ));\n\n /// @dev Executes an exchange method call in the context of signer.\n /// @param salt Arbitrary number to ensure uniqueness of transaction hash.\n /// @param signerAddress Address of transaction signer.\n /// @param data AbiV2 encoded calldata.\n /// @param signature Proof of signer transaction by signer.\n function executeTransaction(\n uint256 salt,\n address signerAddress,\n bytes data,\n bytes signature\n )\n external\n {\n // Prevent reentrancy\n require(\n currentContextAddress == address(0),\n \"REENTRANCY_ILLEGAL\"\n );\n\n bytes32 transactionHash = hashEIP712Message(hashZeroExTransaction(\n salt,\n signerAddress,\n data\n ));\n\n // Validate transaction has not been executed\n require(\n !transactions[transactionHash],\n \"INVALID_TX_HASH\"\n );\n\n // Transaction always valid if signer is sender of transaction\n if (signerAddress != msg.sender) {\n // Validate signature\n require(\n isValidSignature(\n transactionHash,\n signerAddress,\n signature\n ),\n \"INVALID_TX_SIGNATURE\"\n );\n\n // Set the current transaction signer\n currentContextAddress = signerAddress;\n }\n\n // Execute transaction\n transactions[transactionHash] = true;\n require(\n address(this).delegatecall(data),\n \"FAILED_EXECUTION\"\n );\n\n // Reset current transaction signer if it was previously updated\n if (signerAddress != msg.sender) {\n currentContextAddress = address(0);\n }\n }\n\n /// @dev Calculates EIP712 hash of the Transaction.\n /// @param salt Arbitrary number to ensure uniqueness of transaction hash.\n /// @param signerAddress Address of transaction signer.\n /// @param data AbiV2 encoded calldata.\n /// @return EIP712 hash of the Transaction.\n function hashZeroExTransaction(\n uint256 salt,\n address signerAddress,\n bytes memory data\n )\n internal\n pure\n returns (bytes32 result)\n {\n bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;\n bytes32 dataHash = keccak256(data);\n\n // Assembly for more efficiently computing:\n // keccak256(abi.encodePacked(\n // EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH,\n // salt,\n // bytes32(signerAddress),\n // keccak256(data)\n // ));\n\n assembly {\n // Load free memory pointer\n let memPtr := mload(64)\n\n mstore(memPtr, schemaHash) // hash of schema\n mstore(add(memPtr, 32), salt) // salt\n mstore(add(memPtr, 64), and(signerAddress, 0xffffffffffffffffffffffffffffffffffffffff)) // signerAddress\n mstore(add(memPtr, 96), dataHash) // hash of data\n\n // Compute hash\n result := keccak256(memPtr, 128)\n }\n return result;\n }\n\n /// @dev The current function will be called in the context of this address (either 0x transaction signer or `msg.sender`).\n /// If calling a fill function, this address will represent the taker.\n /// If calling a cancel function, this address will represent the maker.\n /// @return Signer of 0x transaction if entry point is `executeTransaction`.\n /// `msg.sender` if entry point is any other function.\n function getCurrentContextAddress()\n internal\n view\n returns (address)\n {\n address currentContextAddress_ = currentContextAddress;\n address contextAddress = currentContextAddress_ == address(0) ? msg.sender : currentContextAddress_;\n return contextAddress;\n }\n}\n", + "2.0.0/protocol/Exchange/MixinWrapperFunctions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../../utils/ReentrancyGuard/ReentrancyGuard.sol\";\nimport \"./libs/LibMath.sol\";\nimport \"./libs/LibOrder.sol\";\nimport \"./libs/LibFillResults.sol\";\nimport \"./libs/LibAbiEncoder.sol\";\nimport \"./mixins/MExchangeCore.sol\";\nimport \"./mixins/MWrapperFunctions.sol\";\n\n\ncontract MixinWrapperFunctions is\n ReentrancyGuard,\n LibMath,\n LibFillResults,\n LibAbiEncoder,\n MExchangeCore,\n MWrapperFunctions\n{\n\n /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n function fillOrKillOrder(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n nonReentrant\n returns (FillResults memory fillResults)\n {\n fillResults = fillOrKillOrderInternal(\n order,\n takerAssetFillAmount,\n signature\n );\n return fillResults;\n }\n\n /// @dev Fills the input order.\n /// Returns false if the transaction would otherwise revert.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderNoThrow(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n returns (FillResults memory fillResults)\n {\n // ABI encode calldata for `fillOrder`\n bytes memory fillOrderCalldata = abiEncodeFillOrder(\n order,\n takerAssetFillAmount,\n signature\n );\n\n // Delegate to `fillOrder` and handle any exceptions gracefully\n assembly {\n let success := delegatecall(\n gas, // forward all gas, TODO: look into gas consumption of assert/throw\n address, // call address of this contract\n add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes)\n mload(fillOrderCalldata), // length of input\n fillOrderCalldata, // write output over input\n 128 // output size is 128 bytes\n )\n if success {\n mstore(fillResults, mload(fillOrderCalldata))\n mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))\n mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))\n mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96)))\n }\n }\n return fillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.\n function batchFillOrders(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n nonReentrant\n returns (FillResults memory totalFillResults)\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n FillResults memory singleFillResults = fillOrderInternal(\n orders[i],\n takerAssetFillAmounts[i],\n signatures[i]\n );\n addFillResults(totalFillResults, singleFillResults);\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrKill.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.\n function batchFillOrKillOrders(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n nonReentrant\n returns (FillResults memory totalFillResults)\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n FillResults memory singleFillResults = fillOrKillOrderInternal(\n orders[i],\n takerAssetFillAmounts[i],\n signatures[i]\n );\n addFillResults(totalFillResults, singleFillResults);\n }\n return totalFillResults;\n }\n\n /// @dev Fills an order with specified parameters and ECDSA signature.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.\n function batchFillOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256[] memory takerAssetFillAmounts,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n takerAssetFillAmounts[i],\n signatures[i]\n );\n addFillResults(totalFillResults, singleFillResults);\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signatures Proofs that orders have been created by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellOrders(\n LibOrder.Order[] memory orders,\n uint256 takerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n nonReentrant\n returns (FillResults memory totalFillResults)\n {\n bytes memory takerAssetData = orders[0].takerAssetData;\n \n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being sold by taker is the same for each order.\n // Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders.\n orders[i].takerAssetData = takerAssetData;\n\n // Calculate the remaining amount of takerAsset to sell\n uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderInternal(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of takerAsset has been sold\n if (totalFillResults.takerAssetFilledAmount >= takerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketSellOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256 takerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n bytes memory takerAssetData = orders[0].takerAssetData;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being sold by taker is the same for each order.\n // Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders.\n orders[i].takerAssetData = takerAssetData;\n\n // Calculate the remaining amount of takerAsset to sell\n uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of takerAsset has been sold\n if (totalFillResults.takerAssetFilledAmount >= takerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple calls of fillOrder until total amount of makerAsset is bought by taker.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyOrders(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n nonReentrant\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.\n orders[i].makerAssetData = makerAssetData;\n\n // Calculate the remaining amount of makerAsset to buy\n uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);\n\n // Convert the remaining amount of makerAsset to buy into remaining amount\n // of takerAsset to sell, assuming entire amount can be sold in the current order\n uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(\n orders[i].takerAssetAmount,\n orders[i].makerAssetAmount,\n remainingMakerAssetFillAmount\n );\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderInternal(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of makerAsset has been bought\n if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.\n /// Returns false if the transaction would otherwise revert.\n /// @param orders Array of order specifications.\n /// @param makerAssetFillAmount Desired amount of makerAsset to buy.\n /// @param signatures Proofs that orders have been signed by makers.\n /// @return Amounts filled and fees paid by makers and taker.\n function marketBuyOrdersNoThrow(\n LibOrder.Order[] memory orders,\n uint256 makerAssetFillAmount,\n bytes[] memory signatures\n )\n public\n returns (FillResults memory totalFillResults)\n {\n bytes memory makerAssetData = orders[0].makerAssetData;\n\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n\n // We assume that asset being bought by taker is the same for each order.\n // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.\n orders[i].makerAssetData = makerAssetData;\n\n // Calculate the remaining amount of makerAsset to buy\n uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);\n\n // Convert the remaining amount of makerAsset to buy into remaining amount\n // of takerAsset to sell, assuming entire amount can be sold in the current order\n uint256 remainingTakerAssetFillAmount = getPartialAmountFloor(\n orders[i].takerAssetAmount,\n orders[i].makerAssetAmount,\n remainingMakerAssetFillAmount\n );\n\n // Attempt to sell the remaining amount of takerAsset\n FillResults memory singleFillResults = fillOrderNoThrow(\n orders[i],\n remainingTakerAssetFillAmount,\n signatures[i]\n );\n\n // Update amounts filled and fees paid by maker and taker\n addFillResults(totalFillResults, singleFillResults);\n\n // Stop execution if the entire amount of makerAsset has been bought\n if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) {\n break;\n }\n }\n return totalFillResults;\n }\n\n /// @dev Synchronously cancels multiple orders in a single transaction.\n /// @param orders Array of order specifications.\n function batchCancelOrders(LibOrder.Order[] memory orders)\n public\n {\n uint256 ordersLength = orders.length;\n for (uint256 i = 0; i != ordersLength; i++) {\n cancelOrder(orders[i]);\n }\n }\n\n /// @dev Fetches information for all passed in orders.\n /// @param orders Array of order specifications.\n /// @return Array of OrderInfo instances that correspond to each order.\n function getOrdersInfo(LibOrder.Order[] memory orders)\n public\n view\n returns (LibOrder.OrderInfo[] memory)\n {\n uint256 ordersLength = orders.length;\n LibOrder.OrderInfo[] memory ordersInfo = new LibOrder.OrderInfo[](ordersLength);\n for (uint256 i = 0; i != ordersLength; i++) {\n ordersInfo[i] = getOrderInfo(orders[i]);\n }\n return ordersInfo;\n }\n\n /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n function fillOrKillOrderInternal(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (FillResults memory fillResults)\n {\n fillResults = fillOrderInternal(\n order,\n takerAssetFillAmount,\n signature\n );\n require(\n fillResults.takerAssetFilledAmount == takerAssetFillAmount,\n \"COMPLETE_FILL_FAILED\"\n );\n return fillResults;\n }\n}\n", "2.0.0/protocol/Exchange/interfaces/IAssetProxyDispatcher.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IAssetProxyDispatcher {\n\n /// @dev Registers an asset proxy to its asset proxy id.\n /// Once an asset proxy is registered, it cannot be unregistered.\n /// @param assetProxy Address of new asset proxy to register.\n function registerAssetProxy(address assetProxy)\n external;\n\n /// @dev Gets an asset proxy.\n /// @param assetProxyId Id of the asset proxy.\n /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.\n function getAssetProxy(bytes4 assetProxyId)\n external\n view\n returns (address);\n}\n", "2.0.0/protocol/Exchange/interfaces/IExchange.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"./IExchangeCore.sol\";\nimport \"./IMatchOrders.sol\";\nimport \"./ISignatureValidator.sol\";\nimport \"./ITransactions.sol\";\nimport \"./IAssetProxyDispatcher.sol\";\nimport \"./IWrapperFunctions.sol\";\n\n\n// solhint-disable no-empty-blocks\ncontract IExchange is\n IExchangeCore,\n IMatchOrders,\n ISignatureValidator,\n ITransactions,\n IAssetProxyDispatcher,\n IWrapperFunctions\n{}\n", "2.0.0/protocol/Exchange/interfaces/IExchangeCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\n\n\ncontract IExchangeCore {\n\n /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch\n /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress).\n /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.\n function cancelOrdersUpTo(uint256 targetOrderEpoch)\n external;\n\n /// @dev Fills the input order.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrder(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n public\n returns (LibFillResults.FillResults memory fillResults);\n\n /// @dev After calling, the order can not be filled anymore.\n /// @param order Order struct containing order specifications.\n function cancelOrder(LibOrder.Order memory order)\n public;\n\n /// @dev Gets information about an order: status, hash, and amount filled.\n /// @param order Order to gather information on.\n /// @return OrderInfo Information about the order and its state.\n /// See LibOrder.OrderInfo for a complete description.\n function getOrderInfo(LibOrder.Order memory order)\n public\n view\n returns (LibOrder.OrderInfo memory orderInfo);\n}\n", @@ -742,13 +791,14 @@ "2.0.0/protocol/Exchange/libs/LibEIP712.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract LibEIP712 {\n // EIP191 header for EIP712 prefix\n string constant internal EIP191_HEADER = \"\\x19\\x01\";\n\n // EIP712 Domain Name value\n string constant internal EIP712_DOMAIN_NAME = \"0x Protocol\";\n\n // EIP712 Domain Version value\n string constant internal EIP712_DOMAIN_VERSION = \"2\";\n\n // Hash of the EIP712 Domain Separator Schema\n bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked(\n \"EIP712Domain(\",\n \"string name,\",\n \"string version,\",\n \"address verifyingContract\",\n \")\"\n ));\n\n // Hash of the EIP712 Domain Separator data\n // solhint-disable-next-line var-name-mixedcase\n bytes32 public EIP712_DOMAIN_HASH;\n\n constructor ()\n public\n {\n EIP712_DOMAIN_HASH = keccak256(abi.encodePacked(\n EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,\n keccak256(bytes(EIP712_DOMAIN_NAME)),\n keccak256(bytes(EIP712_DOMAIN_VERSION)),\n bytes32(address(this))\n ));\n }\n\n /// @dev Calculates EIP712 encoding for a hash struct in this EIP712 Domain.\n /// @param hashStruct The EIP712 hash struct.\n /// @return EIP712 hash applied to this EIP712 Domain.\n function hashEIP712Message(bytes32 hashStruct)\n internal\n view\n returns (bytes32 result)\n {\n bytes32 eip712DomainHash = EIP712_DOMAIN_HASH;\n\n // Assembly for more efficient computing:\n // keccak256(abi.encodePacked(\n // EIP191_HEADER,\n // EIP712_DOMAIN_HASH,\n // hashStruct \n // ));\n\n assembly {\n // Load free memory pointer\n let memPtr := mload(64)\n\n mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000) // EIP191 header\n mstore(add(memPtr, 2), eip712DomainHash) // EIP712 domain hash\n mstore(add(memPtr, 34), hashStruct) // Hash of struct\n\n // Compute hash\n result := keccak256(memPtr, 66)\n }\n return result;\n }\n}\n", "2.0.0/protocol/Exchange/libs/LibExchangeErrors.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\n// solhint-disable\npragma solidity 0.4.24;\n\n\n/// @dev This contract documents the revert reasons used in the Exchange contract.\n/// This contract is intended to serve as a reference, but is not actually used for efficiency reasons.\ncontract LibExchangeErrors {\n\n /// Order validation errors ///\n string constant ORDER_UNFILLABLE = \"ORDER_UNFILLABLE\"; // Order cannot be filled.\n string constant INVALID_MAKER = \"INVALID_MAKER\"; // Invalid makerAddress.\n string constant INVALID_TAKER = \"INVALID_TAKER\"; // Invalid takerAddress.\n string constant INVALID_SENDER = \"INVALID_SENDER\"; // Invalid `msg.sender`.\n string constant INVALID_ORDER_SIGNATURE = \"INVALID_ORDER_SIGNATURE\"; // Signature validation failed. \n \n /// fillOrder validation errors ///\n string constant INVALID_TAKER_AMOUNT = \"INVALID_TAKER_AMOUNT\"; // takerAssetFillAmount cannot equal 0.\n string constant ROUNDING_ERROR = \"ROUNDING_ERROR\"; // Rounding error greater than 0.1% of takerAssetFillAmount. \n \n /// Signature validation errors ///\n string constant INVALID_SIGNATURE = \"INVALID_SIGNATURE\"; // Signature validation failed. \n string constant SIGNATURE_ILLEGAL = \"SIGNATURE_ILLEGAL\"; // Signature type is illegal.\n string constant SIGNATURE_UNSUPPORTED = \"SIGNATURE_UNSUPPORTED\"; // Signature type unsupported.\n \n /// cancelOrdersUptTo errors ///\n string constant INVALID_NEW_ORDER_EPOCH = \"INVALID_NEW_ORDER_EPOCH\"; // Specified salt must be greater than or equal to existing orderEpoch.\n\n /// fillOrKillOrder errors ///\n string constant COMPLETE_FILL_FAILED = \"COMPLETE_FILL_FAILED\"; // Desired takerAssetFillAmount could not be completely filled. \n\n /// matchOrders errors ///\n string constant NEGATIVE_SPREAD_REQUIRED = \"NEGATIVE_SPREAD_REQUIRED\"; // Matched orders must have a negative spread.\n\n /// Transaction errors ///\n string constant REENTRANCY_ILLEGAL = \"REENTRANCY_ILLEGAL\"; // Recursive reentrancy is not allowed. \n string constant INVALID_TX_HASH = \"INVALID_TX_HASH\"; // Transaction has already been executed. \n string constant INVALID_TX_SIGNATURE = \"INVALID_TX_SIGNATURE\"; // Signature validation failed. \n string constant FAILED_EXECUTION = \"FAILED_EXECUTION\"; // Transaction execution failed. \n \n /// registerAssetProxy errors ///\n string constant ASSET_PROXY_ALREADY_EXISTS = \"ASSET_PROXY_ALREADY_EXISTS\"; // AssetProxy with same id already exists.\n\n /// dispatchTransferFrom errors ///\n string constant ASSET_PROXY_DOES_NOT_EXIST = \"ASSET_PROXY_DOES_NOT_EXIST\"; // No assetProxy registered at given id.\n string constant TRANSFER_FAILED = \"TRANSFER_FAILED\"; // Asset transfer unsuccesful.\n\n /// Length validation errors ///\n string constant LENGTH_GREATER_THAN_0_REQUIRED = \"LENGTH_GREATER_THAN_0_REQUIRED\"; // Byte array must have a length greater than 0.\n string constant LENGTH_GREATER_THAN_3_REQUIRED = \"LENGTH_GREATER_THAN_3_REQUIRED\"; // Byte array must have a length greater than 3.\n string constant LENGTH_0_REQUIRED = \"LENGTH_0_REQUIRED\"; // Byte array must have a length of 0.\n string constant LENGTH_65_REQUIRED = \"LENGTH_65_REQUIRED\"; // Byte array must have a length of 65.\n}\n", "2.0.0/protocol/Exchange/libs/LibFillResults.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../../utils/SafeMath/SafeMath.sol\";\n\n\ncontract LibFillResults is\n SafeMath\n{\n\n struct FillResults {\n uint256 makerAssetFilledAmount; // Total amount of makerAsset(s) filled.\n uint256 takerAssetFilledAmount; // Total amount of takerAsset(s) filled.\n uint256 makerFeePaid; // Total amount of ZRX paid by maker(s) to feeRecipient(s).\n uint256 takerFeePaid; // Total amount of ZRX paid by taker to feeRecipients(s).\n }\n\n struct MatchedFillResults {\n FillResults left; // Amounts filled and fees paid of left order.\n FillResults right; // Amounts filled and fees paid of right order.\n uint256 leftMakerAssetSpreadAmount; // Spread between price of left and right order, denominated in the left order's makerAsset, paid to taker.\n }\n\n /// @dev Adds properties of both FillResults instances.\n /// Modifies the first FillResults instance specified.\n /// @param totalFillResults Fill results instance that will be added onto.\n /// @param singleFillResults Fill results instance that will be added to totalFillResults.\n function addFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults)\n internal\n pure\n {\n totalFillResults.makerAssetFilledAmount = safeAdd(totalFillResults.makerAssetFilledAmount, singleFillResults.makerAssetFilledAmount);\n totalFillResults.takerAssetFilledAmount = safeAdd(totalFillResults.takerAssetFilledAmount, singleFillResults.takerAssetFilledAmount);\n totalFillResults.makerFeePaid = safeAdd(totalFillResults.makerFeePaid, singleFillResults.makerFeePaid);\n totalFillResults.takerFeePaid = safeAdd(totalFillResults.takerFeePaid, singleFillResults.takerFeePaid);\n }\n}\n", - "2.0.0/protocol/Exchange/libs/LibMath.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../../utils/SafeMath/SafeMath.sol\";\n\n\ncontract LibMath is\n SafeMath\n{\n\n /// @dev Calculates partial value given a numerator and denominator.\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to calculate partial of.\n /// @return Partial value of target.\n function getPartialAmount(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (uint256 partialAmount)\n {\n partialAmount = safeDiv(\n safeMul(numerator, target),\n denominator\n );\n return partialAmount;\n }\n\n /// @dev Checks if rounding error > 0.1%.\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to multiply with numerator/denominator.\n /// @return Rounding error is present.\n function isRoundingError(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (bool isError)\n {\n uint256 remainder = mulmod(target, numerator, denominator);\n if (remainder == 0) {\n return false; // No rounding error.\n }\n\n uint256 errPercentageTimes1000000 = safeDiv(\n safeMul(remainder, 1000000),\n safeMul(numerator, target)\n );\n isError = errPercentageTimes1000000 > 1000;\n return isError;\n }\n}\n", + "2.0.0/protocol/Exchange/libs/LibMath.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../../../utils/SafeMath/SafeMath.sol\";\n\n\ncontract LibMath is\n SafeMath\n{\n\n /// @dev Calculates partial value given a numerator and denominator rounded down.\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to calculate partial of.\n /// @return Partial value of target rounded down.\n function getPartialAmountFloor(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (uint256 partialAmount)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n \n partialAmount = safeDiv(\n safeMul(numerator, target),\n denominator\n );\n return partialAmount;\n }\n \n /// @dev Calculates partial value given a numerator and denominator rounded down.\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to calculate partial of.\n /// @return Partial value of target rounded up.\n function getPartialAmountCeil(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (uint256 partialAmount)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n \n // safeDiv computes `floor(a / b)`. We use the identity (a, b integer):\n // ceil(a / b) = floor((a + b - 1) / b)\n // To implement `ceil(a / b)` using safeDiv.\n partialAmount = safeDiv(\n safeAdd(\n safeMul(numerator, target),\n safeSub(denominator, 1)\n ),\n denominator\n );\n return partialAmount;\n }\n \n /// @dev Checks if rounding error >= 0.1% when rounding down.\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to multiply with numerator/denominator.\n /// @return Rounding error is present.\n function isRoundingErrorFloor(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (bool isError)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n \n // The absolute rounding error is the difference between the rounded\n // value and the ideal value. The relative rounding error is the\n // absolute rounding error divided by the absolute value of the\n // ideal value. This is undefined when the ideal value is zero.\n //\n // The ideal value is `numerator * target / denominator`.\n // Let's call `numerator * target % denominator` the remainder.\n // The absolute error is `remainder / denominator`.\n //\n // When the ideal value is zero, we require the absolute error to\n // be zero. Fortunately, this is always the case. The ideal value is\n // zero iff `numerator == 0` and/or `target == 0`. In this case the\n // remainder and absolute error are also zero. \n if (target == 0 || numerator == 0) {\n return false;\n }\n \n // Otherwise, we want the relative rounding error to be strictly\n // less than 0.1%.\n // The relative error is `remainder / (numerator * target)`.\n // We want the relative error less than 1 / 1000:\n // remainder / (numerator * denominator) < 1 / 1000\n // or equivalently:\n // 1000 * remainder < numerator * target\n // so we have a rounding error iff:\n // 1000 * remainder >= numerator * target\n uint256 remainder = mulmod(target, numerator, denominator);\n isError = safeMul(1000, remainder) >= safeMul(numerator, target);\n return isError;\n }\n \n /// @dev Checks if rounding error >= 0.1% when rounding up.\n /// @param numerator Numerator.\n /// @param denominator Denominator.\n /// @param target Value to multiply with numerator/denominator.\n /// @return Rounding error is present.\n function isRoundingErrorCeil(\n uint256 numerator,\n uint256 denominator,\n uint256 target\n )\n internal\n pure\n returns (bool isError)\n {\n require(\n denominator > 0,\n \"DIVISION_BY_ZERO\"\n );\n \n // See the comments in `isRoundingError`.\n if (target == 0 || numerator == 0) {\n // When either is zero, the ideal value and rounded value are zero\n // and there is no rounding error. (Although the relative error\n // is undefined.)\n return false;\n }\n // Compute remainder as before\n uint256 remainder = mulmod(target, numerator, denominator);\n // TODO: safeMod\n remainder = safeSub(denominator, remainder) % denominator;\n isError = safeMul(1000, remainder) >= safeMul(numerator, target);\n return isError;\n }\n}\n", "2.0.0/protocol/Exchange/libs/LibOrder.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"./LibEIP712.sol\";\n\n\ncontract LibOrder is\n LibEIP712\n{\n\n // Hash for the EIP712 Order Schema\n bytes32 constant internal EIP712_ORDER_SCHEMA_HASH = keccak256(abi.encodePacked(\n \"Order(\",\n \"address makerAddress,\",\n \"address takerAddress,\",\n \"address feeRecipientAddress,\",\n \"address senderAddress,\",\n \"uint256 makerAssetAmount,\",\n \"uint256 takerAssetAmount,\",\n \"uint256 makerFee,\",\n \"uint256 takerFee,\",\n \"uint256 expirationTimeSeconds,\",\n \"uint256 salt,\",\n \"bytes makerAssetData,\",\n \"bytes takerAssetData\",\n \")\"\n ));\n\n // A valid order remains fillable until it is expired, fully filled, or cancelled.\n // An order's state is unaffected by external factors, like account balances.\n enum OrderStatus {\n INVALID, // Default value\n INVALID_MAKER_ASSET_AMOUNT, // Order does not have a valid maker asset amount\n INVALID_TAKER_ASSET_AMOUNT, // Order does not have a valid taker asset amount\n FILLABLE, // Order is fillable\n EXPIRED, // Order has already expired\n FULLY_FILLED, // Order is fully filled\n CANCELLED // Order has been cancelled\n }\n\n // solhint-disable max-line-length\n struct Order {\n address makerAddress; // Address that created the order. \n address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order. \n address feeRecipientAddress; // Address that will recieve fees when order is filled. \n address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods.\n uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0. \n uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0. \n uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted.\n uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted.\n uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires. \n uint256 salt; // Arbitrary number to facilitate uniqueness of the order's hash. \n bytes makerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The last byte references the id of this proxy.\n bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The last byte references the id of this proxy.\n }\n // solhint-enable max-line-length\n\n struct OrderInfo {\n uint8 orderStatus; // Status that describes order's validity and fillability.\n bytes32 orderHash; // EIP712 hash of the order (see LibOrder.getOrderHash).\n uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled.\n }\n\n /// @dev Calculates Keccak-256 hash of the order.\n /// @param order The order structure.\n /// @return Keccak-256 EIP712 hash of the order.\n function getOrderHash(Order memory order)\n internal\n view\n returns (bytes32 orderHash)\n {\n orderHash = hashEIP712Message(hashOrder(order));\n return orderHash;\n }\n\n /// @dev Calculates EIP712 hash of the order.\n /// @param order The order structure.\n /// @return EIP712 hash of the order.\n function hashOrder(Order memory order)\n internal\n pure\n returns (bytes32 result)\n {\n bytes32 schemaHash = EIP712_ORDER_SCHEMA_HASH;\n bytes32 makerAssetDataHash = keccak256(order.makerAssetData);\n bytes32 takerAssetDataHash = keccak256(order.takerAssetData);\n\n // Assembly for more efficiently computing:\n // keccak256(abi.encodePacked(\n // EIP712_ORDER_SCHEMA_HASH,\n // bytes32(order.makerAddress),\n // bytes32(order.takerAddress),\n // bytes32(order.feeRecipientAddress),\n // bytes32(order.senderAddress),\n // order.makerAssetAmount,\n // order.takerAssetAmount,\n // order.makerFee,\n // order.takerFee,\n // order.expirationTimeSeconds,\n // order.salt,\n // keccak256(order.makerAssetData),\n // keccak256(order.takerAssetData)\n // ));\n\n assembly {\n // Calculate memory addresses that will be swapped out before hashing\n let pos1 := sub(order, 32)\n let pos2 := add(order, 320)\n let pos3 := add(order, 352)\n\n // Backup\n let temp1 := mload(pos1)\n let temp2 := mload(pos2)\n let temp3 := mload(pos3)\n \n // Hash in place\n mstore(pos1, schemaHash)\n mstore(pos2, makerAssetDataHash)\n mstore(pos3, takerAssetDataHash)\n result := keccak256(pos1, 416)\n \n // Restore\n mstore(pos1, temp1)\n mstore(pos2, temp2)\n mstore(pos3, temp3)\n }\n return result;\n }\n}\n", "2.0.0/protocol/Exchange/mixins/MAssetProxyDispatcher.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../interfaces/IAssetProxyDispatcher.sol\";\n\n\ncontract MAssetProxyDispatcher is\n IAssetProxyDispatcher\n{\n\n // Logs registration of new asset proxy\n event AssetProxyRegistered(\n bytes4 id, // Id of new registered AssetProxy.\n address assetProxy // Address of new registered AssetProxy.\n );\n\n /// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.\n /// @param assetData Byte array encoded for the asset.\n /// @param from Address to transfer token from.\n /// @param to Address to transfer token to.\n /// @param amount Amount of token to transfer.\n function dispatchTransferFrom(\n bytes memory assetData,\n address from,\n address to,\n uint256 amount\n )\n internal;\n}\n", - "2.0.0/protocol/Exchange/mixins/MExchangeCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\nimport \"../interfaces/IExchangeCore.sol\";\n\n\ncontract MExchangeCore is\n IExchangeCore\n{\n // Fill event is emitted whenever an order is filled.\n event Fill(\n address indexed makerAddress, // Address that created the order. \n address indexed feeRecipientAddress, // Address that received fees.\n address takerAddress, // Address that filled the order.\n address senderAddress, // Address that called the Exchange contract (msg.sender).\n uint256 makerAssetFilledAmount, // Amount of makerAsset sold by maker and bought by taker. \n uint256 takerAssetFilledAmount, // Amount of takerAsset sold by taker and bought by maker.\n uint256 makerFeePaid, // Amount of ZRX paid to feeRecipient by maker.\n uint256 takerFeePaid, // Amount of ZRX paid to feeRecipient by taker.\n bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getOrderHash).\n bytes makerAssetData, // Encoded data specific to makerAsset. \n bytes takerAssetData // Encoded data specific to takerAsset.\n );\n\n // Cancel event is emitted whenever an individual order is cancelled.\n event Cancel(\n address indexed makerAddress, // Address that created the order. \n address indexed feeRecipientAddress, // Address that would have recieved fees if order was filled. \n address senderAddress, // Address that called the Exchange contract (msg.sender).\n bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getOrderHash).\n bytes makerAssetData, // Encoded data specific to makerAsset. \n bytes takerAssetData // Encoded data specific to takerAsset.\n );\n\n // CancelUpTo event is emitted whenever `cancelOrdersUpTo` is executed succesfully.\n event CancelUpTo(\n address indexed makerAddress, // Orders cancelled must have been created by this address.\n address indexed senderAddress, // Orders cancelled must have a `senderAddress` equal to this address.\n uint256 orderEpoch // Orders with specified makerAddress and senderAddress with a salt less than this value are considered cancelled.\n );\n\n /// @dev Updates state with results of a fill order.\n /// @param order that was filled.\n /// @param takerAddress Address of taker who filled the order.\n /// @param orderTakerAssetFilledAmount Amount of order already filled.\n /// @return fillResults Amounts filled and fees paid by maker and taker.\n function updateFilledState(\n LibOrder.Order memory order,\n address takerAddress,\n bytes32 orderHash,\n uint256 orderTakerAssetFilledAmount,\n LibFillResults.FillResults memory fillResults\n )\n internal;\n\n /// @dev Updates state with results of cancelling an order.\n /// State is only updated if the order is currently fillable.\n /// Otherwise, updating state would have no effect.\n /// @param order that was cancelled.\n /// @param orderHash Hash of order that was cancelled.\n function updateCancelledState(\n LibOrder.Order memory order,\n bytes32 orderHash\n )\n internal;\n\n /// @dev Validates context for fillOrder. Succeeds or throws.\n /// @param order to be filled.\n /// @param orderInfo Status, orderHash, and amount already filled of order.\n /// @param takerAddress Address of order taker.\n /// @param takerAssetFillAmount Desired amount of order to fill by taker.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @param signature Proof that the orders was created by its maker.\n function assertValidFill(\n LibOrder.Order memory order,\n LibOrder.OrderInfo memory orderInfo,\n address takerAddress,\n uint256 takerAssetFillAmount,\n uint256 takerAssetFilledAmount,\n bytes memory signature\n )\n internal\n view;\n\n /// @dev Validates context for cancelOrder. Succeeds or throws.\n /// @param order to be cancelled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n function assertValidCancel(\n LibOrder.Order memory order,\n LibOrder.OrderInfo memory orderInfo\n )\n internal\n view;\n\n /// @dev Calculates amounts filled and fees paid by maker and taker.\n /// @param order to be filled.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @return fillResults Amounts filled and fees paid by maker and taker.\n function calculateFillResults(\n LibOrder.Order memory order,\n uint256 takerAssetFilledAmount\n )\n internal\n pure\n returns (LibFillResults.FillResults memory fillResults);\n\n}\n", + "2.0.0/protocol/Exchange/mixins/MExchangeCore.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\nimport \"../interfaces/IExchangeCore.sol\";\n\n\ncontract MExchangeCore is\n IExchangeCore\n{\n // Fill event is emitted whenever an order is filled.\n event Fill(\n address indexed makerAddress, // Address that created the order. \n address indexed feeRecipientAddress, // Address that received fees.\n address takerAddress, // Address that filled the order.\n address senderAddress, // Address that called the Exchange contract (msg.sender).\n uint256 makerAssetFilledAmount, // Amount of makerAsset sold by maker and bought by taker. \n uint256 takerAssetFilledAmount, // Amount of takerAsset sold by taker and bought by maker.\n uint256 makerFeePaid, // Amount of ZRX paid to feeRecipient by maker.\n uint256 takerFeePaid, // Amount of ZRX paid to feeRecipient by taker.\n bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getOrderHash).\n bytes makerAssetData, // Encoded data specific to makerAsset. \n bytes takerAssetData // Encoded data specific to takerAsset.\n );\n\n // Cancel event is emitted whenever an individual order is cancelled.\n event Cancel(\n address indexed makerAddress, // Address that created the order. \n address indexed feeRecipientAddress, // Address that would have recieved fees if order was filled. \n address senderAddress, // Address that called the Exchange contract (msg.sender).\n bytes32 indexed orderHash, // EIP712 hash of order (see LibOrder.getOrderHash).\n bytes makerAssetData, // Encoded data specific to makerAsset. \n bytes takerAssetData // Encoded data specific to takerAsset.\n );\n\n // CancelUpTo event is emitted whenever `cancelOrdersUpTo` is executed succesfully.\n event CancelUpTo(\n address indexed makerAddress, // Orders cancelled must have been created by this address.\n address indexed senderAddress, // Orders cancelled must have a `senderAddress` equal to this address.\n uint256 orderEpoch // Orders with specified makerAddress and senderAddress with a salt less than this value are considered cancelled.\n );\n\n /// @dev Fills the input order.\n /// @param order Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n /// @return Amounts filled and fees paid by maker and taker.\n function fillOrderInternal(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (LibFillResults.FillResults memory fillResults);\n\n /// @dev Updates state with results of a fill order.\n /// @param order that was filled.\n /// @param takerAddress Address of taker who filled the order.\n /// @param orderTakerAssetFilledAmount Amount of order already filled.\n /// @return fillResults Amounts filled and fees paid by maker and taker.\n function updateFilledState(\n LibOrder.Order memory order,\n address takerAddress,\n bytes32 orderHash,\n uint256 orderTakerAssetFilledAmount,\n LibFillResults.FillResults memory fillResults\n )\n internal;\n\n /// @dev Updates state with results of cancelling an order.\n /// State is only updated if the order is currently fillable.\n /// Otherwise, updating state would have no effect.\n /// @param order that was cancelled.\n /// @param orderHash Hash of order that was cancelled.\n function updateCancelledState(\n LibOrder.Order memory order,\n bytes32 orderHash\n )\n internal;\n \n /// @dev Validates context for fillOrder. Succeeds or throws.\n /// @param order to be filled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n /// @param takerAddress Address of order taker.\n /// @param signature Proof that the orders was created by its maker.\n function assertFillableOrder(\n LibOrder.Order memory order,\n LibOrder.OrderInfo memory orderInfo,\n address takerAddress,\n bytes memory signature\n )\n internal\n view;\n \n /// @dev Validates context for fillOrder. Succeeds or throws.\n /// @param order to be filled.\n /// @param orderInfo Status, orderHash, and amount already filled of order.\n /// @param takerAssetFillAmount Desired amount of order to fill by taker.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @param makerAssetFilledAmount Amount of makerAsset that will be transfered.\n function assertValidFill(\n LibOrder.Order memory order,\n LibOrder.OrderInfo memory orderInfo,\n uint256 takerAssetFillAmount,\n uint256 takerAssetFilledAmount,\n uint256 makerAssetFilledAmount\n )\n internal\n view;\n\n /// @dev Validates context for cancelOrder. Succeeds or throws.\n /// @param order to be cancelled.\n /// @param orderInfo OrderStatus, orderHash, and amount already filled of order.\n function assertValidCancel(\n LibOrder.Order memory order,\n LibOrder.OrderInfo memory orderInfo\n )\n internal\n view;\n\n /// @dev Calculates amounts filled and fees paid by maker and taker.\n /// @param order to be filled.\n /// @param takerAssetFilledAmount Amount of takerAsset that will be filled.\n /// @return fillResults Amounts filled and fees paid by maker and taker.\n function calculateFillResults(\n LibOrder.Order memory order,\n uint256 takerAssetFilledAmount\n )\n internal\n pure\n returns (LibFillResults.FillResults memory fillResults);\n\n}\n", "2.0.0/protocol/Exchange/mixins/MMatchOrders.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\nimport \"../interfaces/IMatchOrders.sol\";\n\n\ncontract MMatchOrders is\n IMatchOrders\n{\n\n /// @dev Validates context for matchOrders. Succeeds or throws.\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n function assertValidMatch(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder\n )\n internal\n pure;\n\n /// @dev Calculates fill amounts for the matched orders.\n /// Each order is filled at their respective price point. However, the calculations are\n /// carried out as though the orders are both being filled at the right order's price point.\n /// The profit made by the leftOrder order goes to the taker (who matched the two orders).\n /// @param leftOrder First order to match.\n /// @param rightOrder Second order to match.\n /// @param leftOrderTakerAssetFilledAmount Amount of left order already filled.\n /// @param rightOrderTakerAssetFilledAmount Amount of right order already filled.\n /// @param matchedFillResults Amounts to fill and fees to pay by maker and taker of matched orders.\n function calculateMatchedFillResults(\n LibOrder.Order memory leftOrder,\n LibOrder.Order memory rightOrder,\n uint256 leftOrderTakerAssetFilledAmount,\n uint256 rightOrderTakerAssetFilledAmount\n )\n internal\n pure\n returns (LibFillResults.MatchedFillResults memory matchedFillResults);\n\n}\n", - "2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../interfaces/ISignatureValidator.sol\";\n\n\ncontract MSignatureValidator is\n ISignatureValidator\n{\n event SignatureValidatorApproval(\n address indexed signerAddress, // Address that approves or disapproves a contract to verify signatures.\n address indexed validatorAddress, // Address of signature validator contract.\n bool approved // Approval or disapproval of validator contract.\n );\n\n // Allowed signature types.\n enum SignatureType {\n Illegal, // 0x00, default value\n Invalid, // 0x01\n EIP712, // 0x02\n EthSign, // 0x03\n Caller, // 0x04\n Wallet, // 0x05\n Validator, // 0x06\n PreSigned, // 0x07\n Trezor, // 0x08\n NSignatureTypes // 0x09, number of signature types. Always leave at end.\n }\n}\n", + "2.0.0/protocol/Exchange/mixins/MSignatureValidator.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"../interfaces/ISignatureValidator.sol\";\n\n\ncontract MSignatureValidator is\n ISignatureValidator\n{\n event SignatureValidatorApproval(\n address indexed signerAddress, // Address that approves or disapproves a contract to verify signatures.\n address indexed validatorAddress, // Address of signature validator contract.\n bool approved // Approval or disapproval of validator contract.\n );\n\n // Allowed signature types.\n enum SignatureType {\n Illegal, // 0x00, default value\n Invalid, // 0x01\n EIP712, // 0x02\n EthSign, // 0x03\n Wallet, // 0x04\n Validator, // 0x05\n PreSigned, // 0x06\n NSignatureTypes // 0x07, number of signature types. Always leave at end.\n }\n\n /// @dev Verifies signature using logic defined by Wallet contract.\n /// @param hash Any 32 byte hash.\n /// @param walletAddress Address that should have signed the given hash\n /// and defines its own signature verification method.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if the address recovered from the provided signature matches the input signer address.\n function isValidWalletSignature(\n bytes32 hash,\n address walletAddress,\n bytes signature\n )\n internal\n view\n returns (bool isValid);\n\n /// @dev Verifies signature using logic defined by Validator contract.\n /// @param validatorAddress Address of validator contract.\n /// @param hash Any 32 byte hash.\n /// @param signerAddress Address that should have signed the given hash.\n /// @param signature Proof that the hash has been signed by signer.\n /// @return True if the address recovered from the provided signature matches the input signer address.\n function isValidValidatorSignature(\n address validatorAddress,\n bytes32 hash,\n address signerAddress,\n bytes signature\n )\n internal\n view\n returns (bool isValid);\n}\n", "2.0.0/protocol/Exchange/mixins/MTransactions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\n\nimport \"../interfaces/ITransactions.sol\";\n\n\ncontract MTransactions is\n ITransactions\n{\n\n /// @dev The current function will be called in the context of this address (either 0x transaction signer or `msg.sender`).\n /// If calling a fill function, this address will represent the taker.\n /// If calling a cancel function, this address will represent the maker.\n /// @return Signer of 0x transaction if entry point is `executeTransaction`.\n /// `msg.sender` if entry point is any other function.\n function getCurrentContextAddress()\n internal\n view\n returns (address);\n}\n", + "2.0.0/protocol/Exchange/mixins/MWrapperFunctions.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\npragma experimental ABIEncoderV2;\n\nimport \"../libs/LibOrder.sol\";\nimport \"../libs/LibFillResults.sol\";\nimport \"../interfaces/IWrapperFunctions.sol\";\n\n\ncontract MWrapperFunctions {\n\n /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.\n /// @param order LibOrder.Order struct containing order specifications.\n /// @param takerAssetFillAmount Desired amount of takerAsset to sell.\n /// @param signature Proof that order has been created by maker.\n function fillOrKillOrderInternal(\n LibOrder.Order memory order,\n uint256 takerAssetFillAmount,\n bytes memory signature\n )\n internal\n returns (LibFillResults.FillResults memory fillResults);\n}\n", "2.0.0/tokens/ERC20Token/ERC20Token.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"./IERC20Token.sol\";\n\n\ncontract ERC20Token is\n IERC20Token\n{\n\n mapping (address => uint256) internal balances;\n mapping (address => mapping (address => uint256)) internal allowed;\n\n uint256 internal _totalSupply;\n\n /// @dev send `value` token to `to` from `msg.sender`\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return True if transfer was successful\n function transfer(address _to, uint256 _value)\n external\n returns (bool)\n {\n require(\n balances[msg.sender] >= _value,\n \"ERC20_INSUFFICIENT_BALANCE\"\n );\n require(\n balances[_to] + _value >= balances[_to],\n \"UINT256_OVERFLOW\"\n );\n\n balances[msg.sender] -= _value;\n balances[_to] += _value;\n\n emit Transfer(\n msg.sender,\n _to,\n _value\n );\n\n return true;\n }\n\n /// @dev send `value` token to `to` from `from` on the condition it is approved by `from`\n /// @param _from The address of the sender\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return True if transfer was successful\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n )\n external\n returns (bool)\n {\n require(\n balances[_from] >= _value,\n \"ERC20_INSUFFICIENT_BALANCE\"\n );\n require(\n allowed[_from][msg.sender] >= _value,\n \"ERC20_INSUFFICIENT_ALLOWANCE\"\n );\n require(\n balances[_to] + _value >= balances[_to],\n \"UINT256_OVERFLOW\"\n );\n\n balances[_to] += _value;\n balances[_from] -= _value;\n allowed[_from][msg.sender] -= _value;\n \n emit Transfer(\n _from,\n _to,\n _value\n );\n \n return true;\n }\n\n /// @dev `msg.sender` approves `_spender` to spend `_value` tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @param _value The amount of wei to be approved for transfer\n /// @return Always true if the call has enough gas to complete execution\n function approve(address _spender, uint256 _value)\n external\n returns (bool)\n {\n allowed[msg.sender][_spender] = _value;\n emit Approval(\n msg.sender,\n _spender,\n _value\n );\n return true;\n }\n\n /// @dev Query total supply of token\n /// @return Total supply of token\n function totalSupply()\n external\n view\n returns (uint256)\n {\n return _totalSupply;\n }\n\n /// @dev Query the balance of owner\n /// @param _owner The address from which the balance will be retrieved\n /// @return Balance of owner\n function balanceOf(address _owner)\n external\n view\n returns (uint256)\n {\n return balances[_owner];\n }\n\n /// @param _owner The address of the account owning tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @return Amount of remaining tokens allowed to spent\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256)\n {\n return allowed[_owner][_spender];\n }\n}\n", "2.0.0/tokens/ERC20Token/IERC20Token.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\ncontract IERC20Token {\n\n // solhint-disable no-simple-event-func-name\n event Transfer(\n address indexed _from,\n address indexed _to,\n uint256 _value\n );\n\n event Approval(\n address indexed _owner,\n address indexed _spender,\n uint256 _value\n );\n\n /// @dev send `value` token to `to` from `msg.sender`\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return True if transfer was successful\n function transfer(address _to, uint256 _value)\n external\n returns (bool);\n\n /// @dev send `value` token to `to` from `from` on the condition it is approved by `from`\n /// @param _from The address of the sender\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return True if transfer was successful\n function transferFrom(\n address _from,\n address _to,\n uint256 _value\n )\n external\n returns (bool);\n \n /// @dev `msg.sender` approves `_spender` to spend `_value` tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @param _value The amount of wei to be approved for transfer\n /// @return Always true if the call has enough gas to complete execution\n function approve(address _spender, uint256 _value)\n external\n returns (bool);\n\n /// @dev Query total supply of token\n /// @return Total supply of token\n function totalSupply()\n external\n view\n returns (uint256);\n \n /// @param _owner The address from which the balance will be retrieved\n /// @return Balance of owner\n function balanceOf(address _owner)\n external\n view\n returns (uint256);\n\n /// @param _owner The address of the account owning tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @return Amount of remaining tokens allowed to spent\n function allowance(address _owner, address _spender)\n external\n view\n returns (uint256);\n}\n", "2.0.0/tokens/ERC721Token/ERC721Token.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\nimport \"./IERC721Token.sol\";\nimport \"./IERC721Receiver.sol\";\nimport \"../../utils/SafeMath/SafeMath.sol\";\n\n\ncontract ERC721Token is\n IERC721Token,\n SafeMath\n{\n // Function selector for ERC721Receiver.onERC721Received\n // 0x150b7a02\n bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256(\"onERC721Received(address,address,uint256,bytes)\"));\n\n // Mapping of tokenId => owner\n mapping (uint256 => address) internal owners;\n\n // Mapping of tokenId => approved address\n mapping (uint256 => address) internal approvals;\n\n // Mapping of owner => number of tokens owned\n mapping (address => uint256) internal balances;\n\n // Mapping of owner => operator => approved\n mapping (address => mapping (address => bool)) internal operatorApprovals;\n\n /// @notice Transfers the ownership of an NFT from one address to another address\n /// @dev Throws unless `msg.sender` is the current owner, an authorized\n /// operator, or the approved address for this NFT. Throws if `_from` is\n /// not the current owner. Throws if `_to` is the zero address. Throws if\n /// `_tokenId` is not a valid NFT. When transfer is complete, this function\n /// checks if `_to` is a smart contract (code size > 0). If so, it calls\n /// `onERC721Received` on `_to` and throws if the return value is not\n /// `bytes4(keccak256(\"onERC721Received(address,address,uint256,bytes)\"))`.\n /// @param _from The current owner of the NFT\n /// @param _to The new owner\n /// @param _tokenId The NFT to transfer\n /// @param _data Additional data with no specified format, sent in call to `_to`\n function safeTransferFrom(\n address _from,\n address _to,\n uint256 _tokenId,\n bytes _data\n )\n external\n {\n transferFrom(\n _from,\n _to,\n _tokenId\n );\n\n uint256 receiverCodeSize;\n assembly {\n receiverCodeSize := extcodesize(_to)\n }\n if (receiverCodeSize > 0) {\n bytes4 selector = IERC721Receiver(_to).onERC721Received(\n msg.sender,\n _from,\n _tokenId,\n _data\n );\n require(\n selector == ERC721_RECEIVED,\n \"ERC721_INVALID_SELECTOR\"\n );\n }\n }\n\n /// @notice Transfers the ownership of an NFT from one address to another address\n /// @dev This works identically to the other function with an extra data parameter,\n /// except this function just sets data to \"\".\n /// @param _from The current owner of the NFT\n /// @param _to The new owner\n /// @param _tokenId The NFT to transfer\n function safeTransferFrom(\n address _from,\n address _to,\n uint256 _tokenId\n )\n external\n {\n transferFrom(\n _from,\n _to,\n _tokenId\n );\n\n uint256 receiverCodeSize;\n assembly {\n receiverCodeSize := extcodesize(_to)\n }\n if (receiverCodeSize > 0) {\n bytes4 selector = IERC721Receiver(_to).onERC721Received(\n msg.sender,\n _from,\n _tokenId,\n \"\"\n );\n require(\n selector == ERC721_RECEIVED,\n \"ERC721_INVALID_SELECTOR\"\n );\n }\n }\n\n /// @notice Change or reaffirm the approved address for an NFT\n /// @dev The zero address indicates there is no approved address.\n /// Throws unless `msg.sender` is the current NFT owner, or an authorized\n /// operator of the current owner.\n /// @param _approved The new approved NFT controller\n /// @param _tokenId The NFT to approve\n function approve(address _approved, uint256 _tokenId)\n external\n {\n address owner = ownerOf(_tokenId);\n require(\n msg.sender == owner || isApprovedForAll(owner, msg.sender),\n \"ERC721_INVALID_SENDER\"\n );\n\n approvals[_tokenId] = _approved;\n emit Approval(\n owner,\n _approved,\n _tokenId\n );\n }\n\n /// @notice Enable or disable approval for a third party (\"operator\") to manage\n /// all of `msg.sender`'s assets\n /// @dev Emits the ApprovalForAll event. The contract MUST allow\n /// multiple operators per owner.\n /// @param _operator Address to add to the set of authorized operators\n /// @param _approved True if the operator is approved, false to revoke approval\n function setApprovalForAll(address _operator, bool _approved)\n external\n {\n operatorApprovals[msg.sender][_operator] = _approved;\n emit ApprovalForAll(\n msg.sender,\n _operator,\n _approved\n );\n }\n \n /// @notice Count all NFTs assigned to an owner\n /// @dev NFTs assigned to the zero address are considered invalid, and this\n /// function throws for queries about the zero address.\n /// @param _owner An address for whom to query the balance\n /// @return The number of NFTs owned by `_owner`, possibly zero\n function balanceOf(address _owner)\n external\n view\n returns (uint256)\n {\n require(\n _owner != address(0),\n \"ERC721_ZERO_OWNER\"\n );\n return balances[_owner];\n }\n\n /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE\n /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE\n /// THEY MAY BE PERMANENTLY LOST\n /// @dev Throws unless `msg.sender` is the current owner, an authorized\n /// operator, or the approved address for this NFT. Throws if `_from` is\n /// not the current owner. Throws if `_to` is the zero address. Throws if\n /// `_tokenId` is not a valid NFT.\n /// @param _from The current owner of the NFT\n /// @param _to The new owner\n /// @param _tokenId The NFT to transfer\n function transferFrom(\n address _from,\n address _to,\n uint256 _tokenId\n )\n public\n {\n require(\n _to != address(0),\n \"ERC721_ZERO_TO_ADDRESS\"\n );\n\n address owner = ownerOf(_tokenId);\n require(\n _from == owner,\n \"ERC721_OWNER_MISMATCH\"\n );\n\n address spender = msg.sender;\n address approvedAddress = getApproved(_tokenId);\n require(\n spender == owner ||\n isApprovedForAll(owner, spender) ||\n approvedAddress == spender,\n \"ERC721_INVALID_SPENDER\"\n );\n\n if (approvedAddress != address(0)) {\n approvals[_tokenId] = address(0);\n }\n\n owners[_tokenId] = _to;\n balances[_from] = safeSub(balances[_from], 1);\n balances[_to] = safeAdd(balances[_to], 1);\n \n emit Transfer(\n _from,\n _to,\n _tokenId\n );\n }\n\n /// @notice Find the owner of an NFT\n /// @dev NFTs assigned to zero address are considered invalid, and queries\n /// about them do throw.\n /// @param _tokenId The identifier for an NFT\n /// @return The address of the owner of the NFT\n function ownerOf(uint256 _tokenId)\n public\n view\n returns (address)\n {\n address owner = owners[_tokenId];\n require(\n owner != address(0),\n \"ERC721_ZERO_OWNER\"\n );\n return owner;\n }\n\n /// @notice Get the approved address for a single NFT\n /// @dev Throws if `_tokenId` is not a valid NFT.\n /// @param _tokenId The NFT to find the approved address for\n /// @return The approved address for this NFT, or the zero address if there is none\n function getApproved(uint256 _tokenId)\n public\n view\n returns (address)\n {\n return approvals[_tokenId];\n }\n\n /// @notice Query if an address is an authorized operator for another address\n /// @param _owner The address that owns the NFTs\n /// @param _operator The address that acts on behalf of the owner\n /// @return True if `_operator` is an approved operator for `_owner`, false otherwise\n function isApprovedForAll(address _owner, address _operator)\n public\n view\n returns (bool)\n {\n return operatorApprovals[_owner][_operator];\n }\n}\n", @@ -758,9 +808,10 @@ "2.0.0/utils/LibBytes/LibBytes.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.24;\n\n\nlibrary LibBytes {\n\n using LibBytes for bytes;\n\n /// @dev Gets the memory address for a byte array.\n /// @param input Byte array to lookup.\n /// @return memoryAddress Memory address of byte array. This\n /// points to the header of the byte array which contains\n /// the length.\n function rawAddress(bytes memory input)\n internal\n pure\n returns (uint256 memoryAddress)\n {\n assembly {\n memoryAddress := input\n }\n return memoryAddress;\n }\n \n /// @dev Gets the memory address for the contents of a byte array.\n /// @param input Byte array to lookup.\n /// @return memoryAddress Memory address of the contents of the byte array.\n function contentAddress(bytes memory input)\n internal\n pure\n returns (uint256 memoryAddress)\n {\n assembly {\n memoryAddress := add(input, 32)\n }\n return memoryAddress;\n }\n\n /// @dev Copies `length` bytes from memory location `source` to `dest`.\n /// @param dest memory address to copy bytes to.\n /// @param source memory address to copy bytes from.\n /// @param length number of bytes to copy.\n function memCopy(\n uint256 dest,\n uint256 source,\n uint256 length\n )\n internal\n pure\n {\n if (length < 32) {\n // Handle a partial word by reading destination and masking\n // off the bits we are interested in.\n // This correctly handles overlap, zero lengths and source == dest\n assembly {\n let mask := sub(exp(256, sub(32, length)), 1)\n let s := and(mload(source), not(mask))\n let d := and(mload(dest), mask)\n mstore(dest, or(s, d))\n }\n } else {\n // Skip the O(length) loop when source == dest.\n if (source == dest) {\n return;\n }\n\n // For large copies we copy whole words at a time. The final\n // word is aligned to the end of the range (instead of after the\n // previous) to handle partial words. So a copy will look like this:\n //\n // ####\n // ####\n // ####\n // ####\n //\n // We handle overlap in the source and destination range by\n // changing the copying direction. This prevents us from\n // overwriting parts of source that we still need to copy.\n //\n // This correctly handles source == dest\n //\n if (source > dest) {\n assembly {\n // We subtract 32 from `sEnd` and `dEnd` because it\n // is easier to compare with in the loop, and these\n // are also the addresses we need for copying the\n // last bytes.\n length := sub(length, 32)\n let sEnd := add(source, length)\n let dEnd := add(dest, length)\n\n // Remember the last 32 bytes of source\n // This needs to be done here and not after the loop\n // because we may have overwritten the last bytes in\n // source already due to overlap.\n let last := mload(sEnd)\n\n // Copy whole words front to back\n // Note: the first check is always true,\n // this could have been a do-while loop.\n // solhint-disable-next-line no-empty-blocks\n for {} lt(source, sEnd) {} {\n mstore(dest, mload(source))\n source := add(source, 32)\n dest := add(dest, 32)\n }\n \n // Write the last 32 bytes\n mstore(dEnd, last)\n }\n } else {\n assembly {\n // We subtract 32 from `sEnd` and `dEnd` because those\n // are the starting points when copying a word at the end.\n length := sub(length, 32)\n let sEnd := add(source, length)\n let dEnd := add(dest, length)\n\n // Remember the first 32 bytes of source\n // This needs to be done here and not after the loop\n // because we may have overwritten the first bytes in\n // source already due to overlap.\n let first := mload(source)\n\n // Copy whole words back to front\n // We use a signed comparisson here to allow dEnd to become\n // negative (happens when source and dest < 32). Valid\n // addresses in local memory will never be larger than\n // 2**255, so they can be safely re-interpreted as signed.\n // Note: the first check is always true,\n // this could have been a do-while loop.\n // solhint-disable-next-line no-empty-blocks\n for {} slt(dest, dEnd) {} {\n mstore(dEnd, mload(sEnd))\n sEnd := sub(sEnd, 32)\n dEnd := sub(dEnd, 32)\n }\n \n // Write the first 32 bytes\n mstore(dest, first)\n }\n }\n }\n }\n\n /// @dev Returns a slices from a byte array.\n /// @param b The byte array to take a slice from.\n /// @param from The starting index for the slice (inclusive).\n /// @param to The final index for the slice (exclusive).\n /// @return result The slice containing bytes at indices [from, to)\n function slice(\n bytes memory b,\n uint256 from,\n uint256 to\n )\n internal\n pure\n returns (bytes memory result)\n {\n require(\n from <= to,\n \"FROM_LESS_THAN_TO_REQUIRED\"\n );\n require(\n to < b.length,\n \"TO_LESS_THAN_LENGTH_REQUIRED\"\n );\n \n // Create a new bytes structure and copy contents\n result = new bytes(to - from);\n memCopy(\n result.contentAddress(),\n b.contentAddress() + from,\n result.length);\n return result;\n }\n \n /// @dev Returns a slice from a byte array without preserving the input.\n /// @param b The byte array to take a slice from. Will be destroyed in the process.\n /// @param from The starting index for the slice (inclusive).\n /// @param to The final index for the slice (exclusive).\n /// @return result The slice containing bytes at indices [from, to)\n /// @dev When `from == 0`, the original array will match the slice. In other cases its state will be corrupted.\n function sliceDestructive(\n bytes memory b,\n uint256 from,\n uint256 to\n )\n internal\n pure\n returns (bytes memory result)\n {\n require(\n from <= to,\n \"FROM_LESS_THAN_TO_REQUIRED\"\n );\n require(\n to < b.length,\n \"TO_LESS_THAN_LENGTH_REQUIRED\"\n );\n \n // Create a new bytes structure around [from, to) in-place.\n assembly {\n result := add(b, from)\n mstore(result, sub(to, from))\n }\n return result;\n }\n\n /// @dev Pops the last byte off of a byte array by modifying its length.\n /// @param b Byte array that will be modified.\n /// @return The byte that was popped off.\n function popLastByte(bytes memory b)\n internal\n pure\n returns (bytes1 result)\n {\n require(\n b.length > 0,\n \"GREATER_THAN_ZERO_LENGTH_REQUIRED\"\n );\n\n // Store last byte.\n result = b[b.length - 1];\n\n assembly {\n // Decrement length of byte array.\n let newLen := sub(mload(b), 1)\n mstore(b, newLen)\n }\n return result;\n }\n\n /// @dev Pops the last 20 bytes off of a byte array by modifying its length.\n /// @param b Byte array that will be modified.\n /// @return The 20 byte address that was popped off.\n function popLast20Bytes(bytes memory b)\n internal\n pure\n returns (address result)\n {\n require(\n b.length >= 20,\n \"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED\"\n );\n\n // Store last 20 bytes.\n result = readAddress(b, b.length - 20);\n\n assembly {\n // Subtract 20 from byte array length.\n let newLen := sub(mload(b), 20)\n mstore(b, newLen)\n }\n return result;\n }\n\n /// @dev Tests equality of two byte arrays.\n /// @param lhs First byte array to compare.\n /// @param rhs Second byte array to compare.\n /// @return True if arrays are the same. False otherwise.\n function equals(\n bytes memory lhs,\n bytes memory rhs\n )\n internal\n pure\n returns (bool equal)\n {\n // Keccak gas cost is 30 + numWords * 6. This is a cheap way to compare.\n // We early exit on unequal lengths, but keccak would also correctly\n // handle this.\n return lhs.length == rhs.length && keccak256(lhs) == keccak256(rhs);\n }\n\n /// @dev Reads an address from a position in a byte array.\n /// @param b Byte array containing an address.\n /// @param index Index in byte array of address.\n /// @return address from byte array.\n function readAddress(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (address result)\n {\n require(\n b.length >= index + 20, // 20 is length of address\n \"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED\"\n );\n\n // Add offset to index:\n // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)\n // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)\n index += 20;\n\n // Read address from array memory\n assembly {\n // 1. Add index to address of bytes array\n // 2. Load 32-byte word from memory\n // 3. Apply 20-byte mask to obtain address\n result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff)\n }\n return result;\n }\n\n /// @dev Writes an address into a specific position in a byte array.\n /// @param b Byte array to insert address into.\n /// @param index Index in byte array of address.\n /// @param input Address to put into byte array.\n function writeAddress(\n bytes memory b,\n uint256 index,\n address input\n )\n internal\n pure\n {\n require(\n b.length >= index + 20, // 20 is length of address\n \"GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED\"\n );\n\n // Add offset to index:\n // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)\n // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)\n index += 20;\n\n // Store address into array memory\n assembly {\n // The address occupies 20 bytes and mstore stores 32 bytes.\n // First fetch the 32-byte word where we'll be storing the address, then\n // apply a mask so we have only the bytes in the word that the address will not occupy.\n // Then combine these bytes with the address and store the 32 bytes back to memory with mstore.\n\n // 1. Add index to address of bytes array\n // 2. Load 32-byte word from memory\n // 3. Apply 12-byte mask to obtain extra bytes occupying word of memory where we'll store the address\n let neighbors := and(\n mload(add(b, index)),\n 0xffffffffffffffffffffffff0000000000000000000000000000000000000000\n )\n \n // Make sure input address is clean.\n // (Solidity does not guarantee this)\n input := and(input, 0xffffffffffffffffffffffffffffffffffffffff)\n\n // Store the neighbors and address into memory\n mstore(add(b, index), xor(input, neighbors))\n }\n }\n\n /// @dev Reads a bytes32 value from a position in a byte array.\n /// @param b Byte array containing a bytes32 value.\n /// @param index Index in byte array of bytes32 value.\n /// @return bytes32 value from byte array.\n function readBytes32(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (bytes32 result)\n {\n require(\n b.length >= index + 32,\n \"GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED\"\n );\n\n // Arrays are prefixed by a 256 bit length parameter\n index += 32;\n\n // Read the bytes32 from array memory\n assembly {\n result := mload(add(b, index))\n }\n return result;\n }\n\n /// @dev Writes a bytes32 into a specific position in a byte array.\n /// @param b Byte array to insert into.\n /// @param index Index in byte array of .\n /// @param input bytes32 to put into byte array.\n function writeBytes32(\n bytes memory b,\n uint256 index,\n bytes32 input\n )\n internal\n pure\n {\n require(\n b.length >= index + 32,\n \"GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED\"\n );\n\n // Arrays are prefixed by a 256 bit length parameter\n index += 32;\n\n // Read the bytes32 from array memory\n assembly {\n mstore(add(b, index), input)\n }\n }\n\n /// @dev Reads a uint256 value from a position in a byte array.\n /// @param b Byte array containing a uint256 value.\n /// @param index Index in byte array of uint256 value.\n /// @return uint256 value from byte array.\n function readUint256(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (uint256 result)\n {\n return uint256(readBytes32(b, index));\n }\n\n /// @dev Writes a uint256 into a specific position in a byte array.\n /// @param b Byte array to insert into.\n /// @param index Index in byte array of .\n /// @param input uint256 to put into byte array.\n function writeUint256(\n bytes memory b,\n uint256 index,\n uint256 input\n )\n internal\n pure\n {\n writeBytes32(b, index, bytes32(input));\n }\n\n /// @dev Reads an unpadded bytes4 value from a position in a byte array.\n /// @param b Byte array containing a bytes4 value.\n /// @param index Index in byte array of bytes4 value.\n /// @return bytes4 value from byte array.\n function readBytes4(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (bytes4 result)\n {\n require(\n b.length >= index + 4,\n \"GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED\"\n );\n assembly {\n result := mload(add(b, 32))\n // Solidity does not require us to clean the trailing bytes.\n // We do it anyway\n result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)\n }\n return result;\n }\n\n /// @dev Reads nested bytes from a specific position.\n /// @dev NOTE: the returned value overlaps with the input value.\n /// Both should be treated as immutable.\n /// @param b Byte array containing nested bytes.\n /// @param index Index of nested bytes.\n /// @return result Nested bytes.\n function readBytesWithLength(\n bytes memory b,\n uint256 index\n )\n internal\n pure\n returns (bytes memory result)\n {\n // Read length of nested bytes\n uint256 nestedBytesLength = readUint256(b, index);\n index += 32;\n\n // Assert length of is valid, given\n // length of nested bytes\n require(\n b.length >= index + nestedBytesLength,\n \"GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED\"\n );\n \n // Return a pointer to the byte array as it exists inside `b`\n assembly {\n result := add(b, index)\n }\n return result;\n }\n\n /// @dev Inserts bytes at a specific position in a byte array.\n /// @param b Byte array to insert into.\n /// @param index Index in byte array of .\n /// @param input bytes to insert.\n function writeBytesWithLength(\n bytes memory b,\n uint256 index,\n bytes memory input\n )\n internal\n pure\n {\n // Assert length of is valid, given\n // length of input\n require(\n b.length >= index + 32 + input.length, // 32 bytes to store length\n \"GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED\"\n );\n\n // Copy into \n memCopy(\n b.contentAddress() + index,\n input.rawAddress(), // includes length of \n input.length + 32 // +32 bytes to store length\n );\n }\n\n /// @dev Performs a deep copy of a byte array onto another byte array of greater than or equal length.\n /// @param dest Byte array that will be overwritten with source bytes.\n /// @param source Byte array to copy onto dest bytes.\n function deepCopyBytes(\n bytes memory dest,\n bytes memory source\n )\n internal\n pure\n {\n uint256 sourceLen = source.length;\n // Dest length must be >= source length, or some bytes would not be copied.\n require(\n dest.length >= sourceLen,\n \"GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED\"\n );\n memCopy(\n dest.contentAddress(),\n source.contentAddress(),\n sourceLen\n );\n }\n}\n", "2.0.0/utils/Ownable/IOwnable.sol": "pragma solidity 0.4.24;\n\n/*\n * Ownable\n *\n * Base contract with an owner.\n * Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner.\n */\n\ncontract IOwnable {\n function transferOwnership(address newOwner)\n public;\n}\n", "2.0.0/utils/Ownable/Ownable.sol": "pragma solidity 0.4.24;\n\n/*\n * Ownable\n *\n * Base contract with an owner.\n * Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner.\n */\n\nimport \"./IOwnable.sol\";\n\n\ncontract Ownable is IOwnable {\n address public owner;\n\n constructor ()\n public\n {\n owner = msg.sender;\n }\n\n modifier onlyOwner() {\n require(\n msg.sender == owner,\n \"ONLY_CONTRACT_OWNER\"\n );\n _;\n }\n\n function transferOwnership(address newOwner)\n public\n onlyOwner\n {\n if (newOwner != address(0)) {\n owner = newOwner;\n }\n }\n}\n", + "2.0.0/utils/ReentrancyGuard/ReentrancyGuard.sol": "/*\n\n Copyright 2018 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\npragma solidity 0.4.24;\n\n\ncontract ReentrancyGuard {\n\n // Locked state of mutex\n bool private locked = false;\n\n /// @dev Functions with this modifer cannot be reentered. The mutex will be locked\n /// before function execution and unlocked after.\n modifier nonReentrant() {\n // Ensure mutex is unlocked\n require(\n !locked,\n \"REENTRANCY_ILLEGAL\"\n );\n\n // Lock mutex before function call\n locked = true;\n\n // Perform function call\n _;\n\n // Unlock mutex after function call\n locked = false;\n }\n}\n", "2.0.0/utils/SafeMath/SafeMath.sol": "pragma solidity 0.4.24;\n\n\ncontract SafeMath {\n function safeMul(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n if (a == 0) {\n return 0;\n }\n uint256 c = a * b;\n require(\n c / a == b,\n \"UINT256_OVERFLOW\"\n );\n return c;\n }\n\n function safeDiv(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n uint256 c = a / b;\n return c;\n }\n\n function safeSub(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n require(\n b <= a,\n \"UINT256_UNDERFLOW\"\n );\n return a - b;\n }\n\n function safeAdd(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n uint256 c = a + b;\n require(\n c >= a,\n \"UINT256_OVERFLOW\"\n );\n return c;\n }\n\n function max64(uint64 a, uint64 b)\n internal\n pure\n returns (uint256)\n {\n return a >= b ? a : b;\n }\n\n function min64(uint64 a, uint64 b)\n internal\n pure\n returns (uint256)\n {\n return a < b ? a : b;\n }\n\n function max256(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n return a >= b ? a : b;\n }\n\n function min256(uint256 a, uint256 b)\n internal\n pure\n returns (uint256)\n {\n return a < b ? a : b;\n }\n}\n" }, - "sourceTreeHashHex": "0xd6d4955beb696b73455fe763da7472fba92d3695d0c0455879babc4fae8663d3", + "sourceTreeHashHex": "0x41709e5597b9934bd39ebbfec8bdc1b39fa4db97e459fb1f13047a8643a68e76", "compiler": { "name": "solc", "version": "soljson-v0.4.24+commit.e67f0147.js", -- cgit From 7271fc0bab45c3d85acee102f2e5cf7ec1249268 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 27 Aug 2018 13:49:44 -0700 Subject: Add getBalancesAndAllowances to wrapper --- .../contract_wrappers/order_validator_wrapper.ts | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'packages') diff --git a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts index b33428306..1da88f624 100644 --- a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts @@ -126,6 +126,31 @@ export class OrderValidatorWrapper extends ContractWrapper { }; return result; } + /** + * Get an array of objects conforming to BalanceAndAllowance containing on-chain balance and allowance for some address and array of assetDatas + * @param address An ethereum address + * @param assetDatas An array of encoded strings that can be decoded by a specified proxy contract + * @return BalanceAndAllowance + */ + public async getBalancesAndAllowancesAsync(address: string, assetDatas: string[]): Promise { + assert.isETHAddressHex('address', address); + _.forEach(assetDatas, (assetData, index) => assert.isHexString(`assetDatas[${index}]`, assetData)); + const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync(); + const balancesAndAllowances = await OrderValidatorContractInstance.getBalancesAndAllowances.callAsync( + address, + assetDatas, + ); + const balances = balancesAndAllowances[0]; + const allowances = balancesAndAllowances[1]; + const result = _.map(balances, (balance, index) => { + const allowance = allowances[index]; + return { + balance, + allowance, + }; + }); + return result; + } /** * Get owner address of tokenId by calling `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token. * @param tokenAddress An ethereum address -- cgit From 0fd44ee2c1c436b7ce31c29259504d37dcd2423c Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 27 Aug 2018 13:53:51 -0700 Subject: Fix broken test --- packages/contract-wrappers/test/order_validator_wrapper_test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/contract-wrappers/test/order_validator_wrapper_test.ts b/packages/contract-wrappers/test/order_validator_wrapper_test.ts index 0226adec7..2fdb00a71 100644 --- a/packages/contract-wrappers/test/order_validator_wrapper_test.ts +++ b/packages/contract-wrappers/test/order_validator_wrapper_test.ts @@ -5,6 +5,7 @@ import { DoneCallback, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import { BlockParamLiteral } from 'ethereum-types'; +import * as _ from 'lodash'; import 'mocha'; import { ContractWrappers, ExchangeCancelEventArgs, ExchangeEvents, ExchangeFillEventArgs, OrderStatus } from '../src'; @@ -102,8 +103,8 @@ describe('OrderValidator', () => { signedOrders, takerAddresses, ); - ordersInfo = ordersAndTradersInfo.ordersInfo; - tradersInfo = ordersAndTradersInfo.tradersInfo; + ordersInfo = _.map(ordersAndTradersInfo, orderAndTraderInfo => orderAndTraderInfo.orderInfo); + tradersInfo = _.map(ordersAndTradersInfo, orderAndTraderInfo => orderAndTraderInfo.traderInfo); }); it('should return the same number of order infos and trader infos as input orders', async () => { expect(ordersInfo.length).to.be.equal(signedOrders.length); -- cgit From ca0dfc6610aae3f675abdc77942291c7fc1f5909 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 27 Aug 2018 13:57:48 -0700 Subject: Add contract-wrappers changelog entry for OrderValidatorWrapper --- packages/contract-wrappers/CHANGELOG.json | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'packages') diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index b3c9b0fb3..b7c89a325 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,4 +1,12 @@ [ + { + "version": "1.0.1-rc.6", + "changes": [ + { + "note": "Add `OrderValidatorWrapper`" + } + ] + }, { "version": "1.0.1-rc.5", "changes": [ -- cgit From 2c846ff1458e23de334434108e8a6c93dbb6f5cf Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 27 Aug 2018 16:06:34 -0700 Subject: Add OrderValidatorWrapper to public interface --- packages/0x.js/src/index.ts | 1 + packages/contract-wrappers/src/index.ts | 1 + 2 files changed, 2 insertions(+) (limited to 'packages') diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts index 7058a898f..6256181b6 100644 --- a/packages/0x.js/src/index.ts +++ b/packages/0x.js/src/index.ts @@ -9,6 +9,7 @@ export { ERC20ProxyWrapper, ERC721ProxyWrapper, ForwarderWrapper, + OrderValidatorWrapper, IndexedFilterValues, BlockRange, ContractWrappersConfig, diff --git a/packages/contract-wrappers/src/index.ts b/packages/contract-wrappers/src/index.ts index 5e691fc21..348854fef 100644 --- a/packages/contract-wrappers/src/index.ts +++ b/packages/contract-wrappers/src/index.ts @@ -6,6 +6,7 @@ export { ExchangeWrapper } from './contract_wrappers/exchange_wrapper'; export { ERC20ProxyWrapper } from './contract_wrappers/erc20_proxy_wrapper'; export { ERC721ProxyWrapper } from './contract_wrappers/erc721_proxy_wrapper'; export { ForwarderWrapper } from './contract_wrappers/forwarder_wrapper'; +export { OrderValidatorWrapper } from './contract_wrappers/order_validator_wrapper'; export { TransactionEncoder } from './utils/transaction_encoder'; -- cgit From 2eab0e30b753fb33729db53d141c8e22017cadec Mon Sep 17 00:00:00 2001 From: Alex Browne Date: Mon, 27 Aug 2018 16:07:38 -0700 Subject: fix(contracts): Catch cases where the actual error differs from the expected error (#1032) * Catch cases where the actual error differs from the expected error * Add tests for testWithReferenceFuncAsync * Small style and comment fixes --- .../contracts/test/utils/test_with_reference.ts | 72 ++++++++++++++-------- .../test/utils_test/test_with_reference.ts | 63 +++++++++++++++++++ 2 files changed, 109 insertions(+), 26 deletions(-) create mode 100644 packages/contracts/test/utils_test/test_with_reference.ts (limited to 'packages') diff --git a/packages/contracts/test/utils/test_with_reference.ts b/packages/contracts/test/utils/test_with_reference.ts index 599b1eed4..b80be4a6c 100644 --- a/packages/contracts/test/utils/test_with_reference.ts +++ b/packages/contracts/test/utils/test_with_reference.ts @@ -6,6 +6,34 @@ import { chaiSetup } from './chai_setup'; chaiSetup.configure(); const expect = chai.expect; +class Value { + public value: T; + constructor(value: T) { + this.value = value; + } +} + +// tslint:disable-next-line: max-classes-per-file +class ErrorMessage { + public error: string; + constructor(message: string) { + this.error = message; + } +} + +type PromiseResult = Value | ErrorMessage; + +// TODO(albrow): This seems like a generic utility function that could exist in +// lodash. We should replace it by a library implementation, or move it to our +// own. +async function evaluatePromise(promise: Promise): Promise> { + try { + return new Value(await promise); + } catch (e) { + return new ErrorMessage(e.message); + } +} + export async function testWithReferenceFuncAsync( referenceFunc: (p0: P0) => Promise, testFunc: (p0: P0) => Promise, @@ -64,39 +92,31 @@ export async function testWithReferenceFuncAsync( testFuncAsync: (...args: any[]) => Promise, values: any[], ): Promise { - let expectedResult: any; - let expectedErr: string | undefined; - try { - expectedResult = await referenceFuncAsync(...values); - } catch (e) { - expectedErr = e.message; - } - let actualResult: any | undefined; - try { - actualResult = await testFuncAsync(...values); - if (!_.isUndefined(expectedErr)) { + // Measure correct behaviour + const expected = await evaluatePromise(referenceFuncAsync(...values)); + + // Measure actual behaviour + const actual = await evaluatePromise(testFuncAsync(...values)); + + // Compare behaviour + if (expected instanceof ErrorMessage) { + // If we expected an error, check if the actual error message contains the + // expected error message. + if (!(actual instanceof ErrorMessage)) { throw new Error( - `Expected error containing ${expectedErr} but got no error\n\tTest case: ${_getTestCaseString( + `Expected error containing ${expected.error} but got no error\n\tTest case: ${_getTestCaseString( referenceFuncAsync, values, )}`, ); } - } catch (e) { - if (_.isUndefined(expectedErr)) { - throw new Error(`${e.message}\n\tTest case: ${_getTestCaseString(referenceFuncAsync, values)}`); - } else { - expect(e.message).to.contain( - expectedErr, - `${e.message}\n\tTest case: ${_getTestCaseString(referenceFuncAsync, values)}`, - ); - } - } - if (!_.isUndefined(actualResult) && !_.isUndefined(expectedResult)) { - expect(actualResult).to.deep.equal( - expectedResult, - `Test case: ${_getTestCaseString(referenceFuncAsync, values)}`, + expect(actual.error).to.contain( + expected.error, + `${actual.error}\n\tTest case: ${_getTestCaseString(referenceFuncAsync, values)}`, ); + } else { + // If we do not expect an error, compare actual and expected directly. + expect(actual).to.deep.equal(expected, `Test case ${_getTestCaseString(referenceFuncAsync, values)}`); } } diff --git a/packages/contracts/test/utils_test/test_with_reference.ts b/packages/contracts/test/utils_test/test_with_reference.ts new file mode 100644 index 000000000..8d633cd1f --- /dev/null +++ b/packages/contracts/test/utils_test/test_with_reference.ts @@ -0,0 +1,63 @@ +import * as chai from 'chai'; + +import { chaiSetup } from '../utils/chai_setup'; +import { testWithReferenceFuncAsync } from '../utils/test_with_reference'; + +chaiSetup.configure(); +const expect = chai.expect; + +async function divAsync(x: number, y: number): Promise { + if (y === 0) { + throw new Error('MathError: divide by zero'); + } + return x / y; +} + +// returns an async function that always returns the given value. +function alwaysValueFunc(value: number): (x: number, y: number) => Promise { + return async (x: number, y: number) => value; +} + +// returns an async function which always throws/rejects with the given error +// message. +function alwaysFailFunc(errMessage: string): (x: number, y: number) => Promise { + return async (x: number, y: number) => { + throw new Error(errMessage); + }; +} + +describe('testWithReferenceFuncAsync', () => { + it('passes when both succeed and actual === expected', async () => { + await testWithReferenceFuncAsync(alwaysValueFunc(0.5), divAsync, [1, 2]); + }); + + it('passes when both fail and actual error contains expected error', async () => { + await testWithReferenceFuncAsync(alwaysFailFunc('divide by zero'), divAsync, [1, 0]); + }); + + it('fails when both succeed and actual !== expected', async () => { + expect(testWithReferenceFuncAsync(alwaysValueFunc(3), divAsync, [1, 2])).to.be.rejectedWith( + 'Test case {"x":1,"y":2}: expected { value: 0.5 } to deeply equal { value: 3 }', + ); + }); + + it('fails when both fail and actual error does not contain expected error', async () => { + expect( + testWithReferenceFuncAsync(alwaysFailFunc('Unexpected math error'), divAsync, [1, 0]), + ).to.be.rejectedWith( + 'MathError: divide by zero\n\tTest case: {"x":1,"y":0}: expected \'MathError: divide by zero\' to include \'Unexpected math error\'', + ); + }); + + it('fails when referenceFunc succeeds and testFunc fails', async () => { + expect(testWithReferenceFuncAsync(alwaysValueFunc(0), divAsync, [1, 0])).to.be.rejectedWith( + 'Test case {"x":1,"y":0}: expected { error: \'MathError: divide by zero\' } to deeply equal { value: 0 }', + ); + }); + + it('fails when referenceFunc fails and testFunc succeeds', async () => { + expect(testWithReferenceFuncAsync(alwaysFailFunc('divide by zero'), divAsync, [1, 2])).to.be.rejectedWith( + 'Expected error containing divide by zero but got no error\n\tTest case: {"x":1,"y":2}', + ); + }); +}); -- cgit From 9c4c4fb19ac14d19a1f13e1a2a9b3b8ca1fa83bc Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Tue, 28 Aug 2018 12:03:21 -0700 Subject: Export missing types and add OrderValidatorWrapper to hidden constructors --- packages/0x.js/src/index.ts | 3 +++ packages/contract-wrappers/src/index.ts | 3 +++ packages/monorepo-scripts/src/doc_gen_configs.ts | 1 + 3 files changed, 7 insertions(+) (limited to 'packages') diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts index 6256181b6..17511f468 100644 --- a/packages/0x.js/src/index.ts +++ b/packages/0x.js/src/index.ts @@ -43,6 +43,9 @@ export { DecodedLogEvent, ExchangeEventArgs, TransactionEncoder, + BalanceAndAllowance, + OrderAndTraderInfo, + TraderInfo, } from '@0xproject/contract-wrappers'; export { OrderWatcher, OnOrderStateChangeCallback, OrderWatcherConfig } from '@0xproject/order-watcher'; diff --git a/packages/contract-wrappers/src/index.ts b/packages/contract-wrappers/src/index.ts index 348854fef..b66d8460e 100644 --- a/packages/contract-wrappers/src/index.ts +++ b/packages/contract-wrappers/src/index.ts @@ -22,6 +22,9 @@ export { OrderInfo, EventCallback, DecodedLogEvent, + BalanceAndAllowance, + OrderAndTraderInfo, + TraderInfo, } from './types'; export { Order, SignedOrder, AssetProxyId } from '@0xproject/types'; diff --git a/packages/monorepo-scripts/src/doc_gen_configs.ts b/packages/monorepo-scripts/src/doc_gen_configs.ts index 6d7560943..102b5d7ca 100644 --- a/packages/monorepo-scripts/src/doc_gen_configs.ts +++ b/packages/monorepo-scripts/src/doc_gen_configs.ts @@ -39,6 +39,7 @@ export const docGenConfigs: DocGenConfigs = { 'EtherTokenWrapper', 'ExchangeWrapper', 'ForwarderWrapper', + 'OrderValidatorWrapper', 'TransactionEncoder', ], // Some types are not explicitly part of the public interface like params, return values, etc... But we still -- cgit From 14fdb71a716cb95bc1f6933db9057d23f3c41909 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Tue, 28 Aug 2018 13:00:49 -0700 Subject: safeGetPartialAmount (#1035) * Added Test "Should transfer correct amounts when left order is fully filled and values pass isRoundingErrorCeil but fail isRoundingErrorFloor" * Added RoundingError exception to reference function for getPartialAmount * Added RoundingError exception to reference function for getPartialAmount * Added isRoundingErrorCeil to getPartialAmountCeil reference funtion * Computed new values for "Should give right maker a better buy price when correct price is not integral" that does not have a rounding error * Almost all tests for match orders are passing after adding isRoundingErrorCeil check * WIP commit: Added rounding error checks to getPartialAmount * WIP commit: Added rounding error checks to getPartialAmount * Use safe versions of getPartialAmount * Update Exchange internals tests * Run linter * Found new values for "Should transfer correct amounts when right order fill amount deviates from amount derived by `Exchange.fillOrder`" * Fixed merge conflicts * Run all tests * Cleaned up some comments on match Orders tests * Fix tests for geth --- .../2.0.0/protocol/Exchange/MixinExchangeCore.sol | 16 +- .../2.0.0/protocol/Exchange/MixinMatchOrders.sol | 16 +- .../src/2.0.0/protocol/Exchange/libs/LibMath.sol | 82 +++++++- .../ReentrantERC20Token/ReentrantERC20Token.sol | 1 + .../TestExchangeInternals.sol | 36 ++++ packages/contracts/test/exchange/internal.ts | 181 ++++++++++++----- packages/contracts/test/exchange/match_orders.ts | 220 +++++++++++++++++---- 7 files changed, 442 insertions(+), 110 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index be163ec97..64b1f7665 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -404,16 +404,6 @@ contract MixinExchangeCore is safeMul(order.makerAssetAmount, takerAssetFilledAmount), "INVALID_FILL_PRICE" ); - - // Validate fill order rounding - require( - !isRoundingErrorFloor( - takerAssetFilledAmount, - order.takerAssetAmount, - order.makerAssetAmount - ), - "ROUNDING_ERROR" - ); } /// @dev Validates context for cancelOrder. Succeeds or throws. @@ -463,17 +453,17 @@ contract MixinExchangeCore is { // Compute proportional transfer amounts fillResults.takerAssetFilledAmount = takerAssetFilledAmount; - fillResults.makerAssetFilledAmount = getPartialAmountFloor( + fillResults.makerAssetFilledAmount = safeGetPartialAmountFloor( takerAssetFilledAmount, order.takerAssetAmount, order.makerAssetAmount ); - fillResults.makerFeePaid = getPartialAmountFloor( + fillResults.makerFeePaid = safeGetPartialAmountFloor( takerAssetFilledAmount, order.takerAssetAmount, order.makerFee ); - fillResults.takerFeePaid = getPartialAmountFloor( + fillResults.takerFeePaid = safeGetPartialAmountFloor( takerAssetFilledAmount, order.takerAssetAmount, order.takerFee diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol index 075a610b5..b4f6bdb26 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol @@ -177,13 +177,13 @@ contract MixinMatchOrders is { // Derive maker asset amounts for left & right orders, given store taker assert amounts uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount); - uint256 leftMakerAssetAmountRemaining = getPartialAmountFloor( + uint256 leftMakerAssetAmountRemaining = safeGetPartialAmountFloor( leftOrder.makerAssetAmount, leftOrder.takerAssetAmount, leftTakerAssetAmountRemaining ); uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount); - uint256 rightMakerAssetAmountRemaining = getPartialAmountFloor( + uint256 rightMakerAssetAmountRemaining = safeGetPartialAmountFloor( rightOrder.makerAssetAmount, rightOrder.takerAssetAmount, rightTakerAssetAmountRemaining @@ -205,7 +205,7 @@ contract MixinMatchOrders is matchedFillResults.left.takerAssetFilledAmount = matchedFillResults.right.makerAssetFilledAmount; // Round down to ensure the maker's exchange rate does not exceed the price specified by the order. // We favor the maker when the exchange rate must be rounded. - matchedFillResults.left.makerAssetFilledAmount = getPartialAmountFloor( + matchedFillResults.left.makerAssetFilledAmount = safeGetPartialAmountFloor( leftOrder.makerAssetAmount, leftOrder.takerAssetAmount, matchedFillResults.left.takerAssetFilledAmount @@ -217,7 +217,7 @@ contract MixinMatchOrders is matchedFillResults.right.makerAssetFilledAmount = matchedFillResults.left.takerAssetFilledAmount; // Round up to ensure the maker's exchange rate does not exceed the price specified by the order. // We favor the maker when the exchange rate must be rounded. - matchedFillResults.right.takerAssetFilledAmount = getPartialAmountCeil( + matchedFillResults.right.takerAssetFilledAmount = safeGetPartialAmountCeil( rightOrder.takerAssetAmount, rightOrder.makerAssetAmount, matchedFillResults.right.makerAssetFilledAmount @@ -231,24 +231,24 @@ contract MixinMatchOrders is ); // Compute fees for left order - matchedFillResults.left.makerFeePaid = getPartialAmountFloor( + matchedFillResults.left.makerFeePaid = safeGetPartialAmountFloor( matchedFillResults.left.makerAssetFilledAmount, leftOrder.makerAssetAmount, leftOrder.makerFee ); - matchedFillResults.left.takerFeePaid = getPartialAmountFloor( + matchedFillResults.left.takerFeePaid = safeGetPartialAmountFloor( matchedFillResults.left.takerAssetFilledAmount, leftOrder.takerAssetAmount, leftOrder.takerFee ); // Compute fees for right order - matchedFillResults.right.makerFeePaid = getPartialAmountFloor( + matchedFillResults.right.makerFeePaid = safeGetPartialAmountFloor( matchedFillResults.right.makerAssetFilledAmount, rightOrder.makerAssetAmount, rightOrder.makerFee ); - matchedFillResults.right.takerFeePaid = getPartialAmountFloor( + matchedFillResults.right.takerFeePaid = safeGetPartialAmountFloor( matchedFillResults.right.takerAssetFilledAmount, rightOrder.takerAssetAmount, rightOrder.takerFee diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol index 0e0fba5d2..57fd53f29 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol @@ -26,11 +26,48 @@ contract LibMath is { /// @dev Calculates partial value given a numerator and denominator rounded down. + /// Reverts if rounding error is >= 0.1% /// @param numerator Numerator. /// @param denominator Denominator. /// @param target Value to calculate partial of. /// @return Partial value of target rounded down. - function getPartialAmountFloor( + function safeGetPartialAmountFloor( + uint256 numerator, + uint256 denominator, + uint256 target + ) + internal + pure + returns (uint256 partialAmount) + { + require( + denominator > 0, + "DIVISION_BY_ZERO" + ); + + require( + !isRoundingErrorFloor( + numerator, + denominator, + target + ), + "ROUNDING_ERROR" + ); + + partialAmount = safeDiv( + safeMul(numerator, target), + denominator + ); + return partialAmount; + } + + /// @dev Calculates partial value given a numerator and denominator rounded down. + /// Reverts if rounding error is >= 0.1% + /// @param numerator Numerator. + /// @param denominator Denominator. + /// @param target Value to calculate partial of. + /// @return Partial value of target rounded up. + function safeGetPartialAmountCeil( uint256 numerator, uint256 denominator, uint256 target @@ -43,7 +80,48 @@ contract LibMath is denominator > 0, "DIVISION_BY_ZERO" ); + + require( + !isRoundingErrorCeil( + numerator, + denominator, + target + ), + "ROUNDING_ERROR" + ); + // safeDiv computes `floor(a / b)`. We use the identity (a, b integer): + // ceil(a / b) = floor((a + b - 1) / b) + // To implement `ceil(a / b)` using safeDiv. + partialAmount = safeDiv( + safeAdd( + safeMul(numerator, target), + safeSub(denominator, 1) + ), + denominator + ); + return partialAmount; + } + + /// @dev Calculates partial value given a numerator and denominator rounded down. + /// @param numerator Numerator. + /// @param denominator Denominator. + /// @param target Value to calculate partial of. + /// @return Partial value of target rounded down. + function getPartialAmountFloor( + uint256 numerator, + uint256 denominator, + uint256 target + ) + internal + pure + returns (uint256 partialAmount) + { + require( + denominator > 0, + "DIVISION_BY_ZERO" + ); + partialAmount = safeDiv( safeMul(numerator, target), denominator @@ -69,7 +147,7 @@ contract LibMath is denominator > 0, "DIVISION_BY_ZERO" ); - + // safeDiv computes `floor(a / b)`. We use the identity (a, b integer): // ceil(a / b) = floor((a + b - 1) / b) // To implement `ceil(a / b)` using safeDiv. diff --git a/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol b/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol index 8bfdd2e66..4f4e28302 100644 --- a/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol +++ b/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol @@ -25,6 +25,7 @@ import "../../protocol/Exchange/interfaces/IExchange.sol"; import "../../protocol/Exchange/libs/LibOrder.sol"; +// solhint-disable no-unused-vars contract ReentrantERC20Token is ERC20Token { diff --git a/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol b/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol index da9313e02..27187f8f8 100644 --- a/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol +++ b/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol @@ -62,6 +62,42 @@ contract TestExchangeInternals is return calculateFillResults(order, takerAssetFilledAmount); } + /// @dev Calculates partial value given a numerator and denominator. + /// Reverts if rounding error is >= 0.1% + /// @param numerator Numerator. + /// @param denominator Denominator. + /// @param target Value to calculate partial of. + /// @return Partial value of target. + function publicSafeGetPartialAmountFloor( + uint256 numerator, + uint256 denominator, + uint256 target + ) + public + pure + returns (uint256 partialAmount) + { + return safeGetPartialAmountFloor(numerator, denominator, target); + } + + /// @dev Calculates partial value given a numerator and denominator. + /// Reverts if rounding error is >= 0.1% + /// @param numerator Numerator. + /// @param denominator Denominator. + /// @param target Value to calculate partial of. + /// @return Partial value of target. + function publicSafeGetPartialAmountCeil( + uint256 numerator, + uint256 denominator, + uint256 target + ) + public + pure + returns (uint256 partialAmount) + { + return safeGetPartialAmountCeil(numerator, denominator, target); + } + /// @dev Calculates partial value given a numerator and denominator. /// @param numerator Numerator. /// @param denominator Denominator. diff --git a/packages/contracts/test/exchange/internal.ts b/packages/contracts/test/exchange/internal.ts index de381fca3..c5d2fc58f 100644 --- a/packages/contracts/test/exchange/internal.ts +++ b/packages/contracts/test/exchange/internal.ts @@ -48,9 +48,9 @@ const overflowErrorForCall = new Error(RevertReason.Uint256Overflow); describe('Exchange core internal functions', () => { let testExchange: TestExchangeInternalsContract; - let invalidOpcodeErrorForCall: Error | undefined; let overflowErrorForSendTransaction: Error | undefined; let divisionByZeroErrorForCall: Error | undefined; + let roundingErrorForCall: Error | undefined; before(async () => { await blockchainLifecycle.startAsync(); @@ -68,12 +68,67 @@ describe('Exchange core internal functions', () => { await getRevertReasonOrErrorMessageForSendTransactionAsync(RevertReason.Uint256Overflow), ); divisionByZeroErrorForCall = new Error(RevertReason.DivisionByZero); - invalidOpcodeErrorForCall = new Error(await getInvalidOpcodeErrorMessageForCallAsync()); + roundingErrorForCall = new Error(RevertReason.RoundingError); }); // Note(albrow): Don't forget to add beforeEach and afterEach calls to reset // the blockchain state for any tests which modify it! - async function referenceGetPartialAmountFloorAsync( + async function referenceIsRoundingErrorFloorAsync( + numerator: BigNumber, + denominator: BigNumber, + target: BigNumber, + ): Promise { + if (denominator.eq(0)) { + throw divisionByZeroErrorForCall; + } + if (numerator.eq(0)) { + return false; + } + if (target.eq(0)) { + return false; + } + const product = numerator.mul(target); + const remainder = product.mod(denominator); + const remainderTimes1000 = remainder.mul('1000'); + const isError = remainderTimes1000.gte(product); + if (product.greaterThan(MAX_UINT256)) { + throw overflowErrorForCall; + } + if (remainderTimes1000.greaterThan(MAX_UINT256)) { + throw overflowErrorForCall; + } + return isError; + } + + async function referenceIsRoundingErrorCeilAsync( + numerator: BigNumber, + denominator: BigNumber, + target: BigNumber, + ): Promise { + if (denominator.eq(0)) { + throw divisionByZeroErrorForCall; + } + if (numerator.eq(0)) { + return false; + } + if (target.eq(0)) { + return false; + } + const product = numerator.mul(target); + const remainder = product.mod(denominator); + const error = denominator.sub(remainder).mod(denominator); + const errorTimes1000 = error.mul('1000'); + const isError = errorTimes1000.gte(product); + if (product.greaterThan(MAX_UINT256)) { + throw overflowErrorForCall; + } + if (errorTimes1000.greaterThan(MAX_UINT256)) { + throw overflowErrorForCall; + } + return isError; + } + + async function referenceSafeGetPartialAmountFloorAsync( numerator: BigNumber, denominator: BigNumber, target: BigNumber, @@ -81,6 +136,10 @@ describe('Exchange core internal functions', () => { if (denominator.eq(0)) { throw divisionByZeroErrorForCall; } + const isRoundingError = await referenceIsRoundingErrorFloorAsync(numerator, denominator, target); + if (isRoundingError) { + throw roundingErrorForCall; + } const product = numerator.mul(target); if (product.greaterThan(MAX_UINT256)) { throw overflowErrorForCall; @@ -163,18 +222,18 @@ describe('Exchange core internal functions', () => { // implementation or the Solidity implementation of // calculateFillResults. return { - makerAssetFilledAmount: await referenceGetPartialAmountFloorAsync( + makerAssetFilledAmount: await referenceSafeGetPartialAmountFloorAsync( takerAssetFilledAmount, orderTakerAssetAmount, otherAmount, ), takerAssetFilledAmount, - makerFeePaid: await referenceGetPartialAmountFloorAsync( + makerFeePaid: await referenceSafeGetPartialAmountFloorAsync( takerAssetFilledAmount, orderTakerAssetAmount, otherAmount, ), - takerFeePaid: await referenceGetPartialAmountFloorAsync( + takerFeePaid: await referenceSafeGetPartialAmountFloorAsync( takerAssetFilledAmount, orderTakerAssetAmount, otherAmount, @@ -198,6 +257,20 @@ describe('Exchange core internal functions', () => { }); describe('getPartialAmountFloor', async () => { + async function referenceGetPartialAmountFloorAsync( + numerator: BigNumber, + denominator: BigNumber, + target: BigNumber, + ): Promise { + if (denominator.eq(0)) { + throw divisionByZeroErrorForCall; + } + const product = numerator.mul(target); + if (product.greaterThan(MAX_UINT256)) { + throw overflowErrorForCall; + } + return product.dividedToIntegerBy(denominator); + } async function testGetPartialAmountFloorAsync( numerator: BigNumber, denominator: BigNumber, @@ -206,7 +279,7 @@ describe('Exchange core internal functions', () => { return testExchange.publicGetPartialAmountFloor.callAsync(numerator, denominator, target); } await testCombinatoriallyWithReferenceFuncAsync( - 'getPartialAmount', + 'getPartialAmountFloor', referenceGetPartialAmountFloorAsync, testGetPartialAmountFloorAsync, [uint256Values, uint256Values, uint256Values], @@ -250,76 +323,80 @@ describe('Exchange core internal functions', () => { ); }); - describe('isRoundingError', async () => { - async function referenceIsRoundingErrorAsync( + describe('safeGetPartialAmountFloor', async () => { + async function testSafeGetPartialAmountFloorAsync( numerator: BigNumber, denominator: BigNumber, target: BigNumber, - ): Promise { + ): Promise { + return testExchange.publicSafeGetPartialAmountFloor.callAsync(numerator, denominator, target); + } + await testCombinatoriallyWithReferenceFuncAsync( + 'safeGetPartialAmountFloor', + referenceSafeGetPartialAmountFloorAsync, + testSafeGetPartialAmountFloorAsync, + [uint256Values, uint256Values, uint256Values], + ); + }); + + describe('safeGetPartialAmountCeil', async () => { + async function referenceSafeGetPartialAmountCeilAsync( + numerator: BigNumber, + denominator: BigNumber, + target: BigNumber, + ): Promise { if (denominator.eq(0)) { throw divisionByZeroErrorForCall; } - if (numerator.eq(0)) { - return false; - } - if (target.eq(0)) { - return false; + const isRoundingError = await referenceIsRoundingErrorCeilAsync(numerator, denominator, target); + if (isRoundingError) { + throw roundingErrorForCall; } const product = numerator.mul(target); - const remainder = product.mod(denominator); - const remainderTimes1000 = remainder.mul('1000'); - const isError = remainderTimes1000.gt(product); - if (product.greaterThan(MAX_UINT256)) { + const offset = product.add(denominator.sub(1)); + if (offset.greaterThan(MAX_UINT256)) { throw overflowErrorForCall; } - if (remainderTimes1000.greaterThan(MAX_UINT256)) { - throw overflowErrorForCall; + const result = offset.dividedToIntegerBy(denominator); + if (product.mod(denominator).eq(0)) { + expect(result.mul(denominator)).to.be.bignumber.eq(product); + } else { + expect(result.mul(denominator)).to.be.bignumber.gt(product); } - return isError; + return result; } - async function testIsRoundingErrorAsync( + async function testSafeGetPartialAmountCeilAsync( numerator: BigNumber, denominator: BigNumber, target: BigNumber, - ): Promise { - return testExchange.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target); + ): Promise { + return testExchange.publicSafeGetPartialAmountCeil.callAsync(numerator, denominator, target); } await testCombinatoriallyWithReferenceFuncAsync( - 'isRoundingError', - referenceIsRoundingErrorAsync, - testIsRoundingErrorAsync, + 'safeGetPartialAmountCeil', + referenceSafeGetPartialAmountCeilAsync, + testSafeGetPartialAmountCeilAsync, [uint256Values, uint256Values, uint256Values], ); }); - describe('isRoundingErrorCeil', async () => { - async function referenceIsRoundingErrorAsync( + describe('isRoundingErrorFloor', async () => { + async function testIsRoundingErrorFloorAsync( numerator: BigNumber, denominator: BigNumber, target: BigNumber, ): Promise { - if (denominator.eq(0)) { - throw divisionByZeroErrorForCall; - } - if (numerator.eq(0)) { - return false; - } - if (target.eq(0)) { - return false; - } - const product = numerator.mul(target); - const remainder = product.mod(denominator); - const error = denominator.sub(remainder).mod(denominator); - const errorTimes1000 = error.mul('1000'); - const isError = errorTimes1000.gt(product); - if (product.greaterThan(MAX_UINT256)) { - throw overflowErrorForCall; - } - if (errorTimes1000.greaterThan(MAX_UINT256)) { - throw overflowErrorForCall; - } - return isError; + return testExchange.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target); } + await testCombinatoriallyWithReferenceFuncAsync( + 'isRoundingErrorFloor', + referenceIsRoundingErrorFloorAsync, + testIsRoundingErrorFloorAsync, + [uint256Values, uint256Values, uint256Values], + ); + }); + + describe('isRoundingErrorCeil', async () => { async function testIsRoundingErrorCeilAsync( numerator: BigNumber, denominator: BigNumber, @@ -329,7 +406,7 @@ describe('Exchange core internal functions', () => { } await testCombinatoriallyWithReferenceFuncAsync( 'isRoundingErrorCeil', - referenceIsRoundingErrorAsync, + referenceIsRoundingErrorCeilAsync, testIsRoundingErrorCeilAsync, [uint256Values, uint256Values, uint256Values], ); diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index 8732e20ba..554c456cc 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -3,6 +3,7 @@ import { assetDataUtils } from '@0xproject/order-utils'; import { RevertReason } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import * as chai from 'chai'; import * as _ from 'lodash'; import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_erc20_token'; @@ -11,8 +12,10 @@ import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_prox import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy'; import { ExchangeContract } from '../../generated_contract_wrappers/exchange'; import { ReentrantERC20TokenContract } from '../../generated_contract_wrappers/reentrant_erc20_token'; +import { TestExchangeInternalsContract } from '../../generated_contract_wrappers/test_exchange_internals'; import { artifacts } from '../utils/artifacts'; import { expectTransactionFailedAsync } from '../utils/assertions'; +import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; import { ERC721Wrapper } from '../utils/erc721_wrapper'; @@ -23,6 +26,8 @@ import { ERC20BalancesByOwner, ERC721TokenIdsByOwner } from '../utils/types'; import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); +chaiSetup.configure(); +const expect = chai.expect; describe('matchOrders', () => { let makerAddressLeft: string; @@ -58,6 +63,8 @@ describe('matchOrders', () => { let matchOrderTester: MatchOrderTester; + let testExchange: TestExchangeInternalsContract; + before(async () => { await blockchainLifecycle.startAsync(); }); @@ -160,6 +167,11 @@ describe('matchOrders', () => { orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParamsRight); // Set match order tester matchOrderTester = new MatchOrderTester(exchangeWrapper, erc20Wrapper, erc721Wrapper, zrxToken.address); + testExchange = await TestExchangeInternalsContract.deployFrom0xArtifactAsync( + artifacts.TestExchangeInternals, + provider, + txDefaults, + ); }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -173,39 +185,170 @@ describe('matchOrders', () => { erc721TokenIdsByOwner = await erc721Wrapper.getBalancesAsync(); }); - it('Should give right maker a better price when correct price is not integral', async () => { + it('Should transfer correct amounts when right order is fully filled and values pass isRoundingErrorFloor but fail isRoundingErrorCeil', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAddress: makerAddressLeft, + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(17), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(98), 0), + feeRecipientAddress: feeRecipientAddressLeft, + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAddress: makerAddressRight, + makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), + feeRecipientAddress: feeRecipientAddressRight, + }); + // Assert is rounding error ceil & not rounding error floor + // These assertions are taken from MixinMatchOrders::calculateMatchedFillResults + // The rounding error is derived computating how much the left maker will sell. + const numerator = signedOrderLeft.makerAssetAmount; + const denominator = signedOrderLeft.takerAssetAmount; + const target = signedOrderRight.makerAssetAmount; + const isRoundingErrorCeil = await testExchange.publicIsRoundingErrorCeil.callAsync( + numerator, + denominator, + target, + ); + expect(isRoundingErrorCeil).to.be.true(); + const isRoundingErrorFloor = await testExchange.publicIsRoundingErrorFloor.callAsync( + numerator, + denominator, + target, + ); + expect(isRoundingErrorFloor).to.be.false(); + // Match signedOrderLeft with signedOrderRight + // Note that the left maker received a slightly better sell price. + // This is intentional; see note in MixinMatchOrders.calculateMatchedFillResults. + // Because the left maker received a slightly more favorable sell price, the fee + // paid by the left taker is slightly higher than that paid by the left maker. + // Fees can be thought of as a tax paid by the seller, derived from the sale price. + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.4705882352941176'), 16), // 76.47% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), 0), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.5306122448979591'), 16), // 76.53% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + }; + await matchOrderTester.matchOrdersAndAssertEffectsAsync( + signedOrderLeft, + signedOrderRight, + takerAddress, + erc20BalancesByOwner, + erc721TokenIdsByOwner, + expectedTransferAmounts, + ); + }); + + it('Should transfer correct amounts when left order is fully filled and values pass isRoundingErrorCeil but fail isRoundingErrorFloor', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAddress: makerAddressLeft, + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(15), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 0), + feeRecipientAddress: feeRecipientAddressLeft, + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAddress: makerAddressRight, + makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), + takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(97), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(14), 0), + feeRecipientAddress: feeRecipientAddressRight, + }); + // Assert is rounding error floor & not rounding error ceil + // These assertions are taken from MixinMatchOrders::calculateMatchedFillResults + // The rounding error is derived computating how much the right maker will buy. + const numerator = signedOrderRight.takerAssetAmount; + const denominator = signedOrderRight.makerAssetAmount; + const target = signedOrderLeft.takerAssetAmount; + const isRoundingErrorFloor = await testExchange.publicIsRoundingErrorFloor.callAsync( + numerator, + denominator, + target, + ); + expect(isRoundingErrorFloor).to.be.true(); + const isRoundingErrorCeil = await testExchange.publicIsRoundingErrorCeil.callAsync( + numerator, + denominator, + target, + ); + expect(isRoundingErrorCeil).to.be.false(); + // Match signedOrderLeft with signedOrderRight + // Note that the right maker received a slightly better purchase price. + // This is intentional; see note in MixinMatchOrders.calculateMatchedFillResults. + // Because the right maker received a slightly more favorable buy price, the fee + // paid by the right taker is slightly higher than that paid by the right maker. + // Fees can be thought of as a tax paid by the seller, derived from the sale price. + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(15), 0), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 0), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(90), 0), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber('92.7835051546391752'), 16), // 92.78% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 0), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber('92.8571428571428571'), 16), // 92.85% + }; + await matchOrderTester.matchOrdersAndAssertEffectsAsync( + signedOrderLeft, + signedOrderRight, + takerAddress, + erc20BalancesByOwner, + erc721TokenIdsByOwner, + expectedTransferAmounts, + ); + }); + + it('Should give right maker a better buy price when rounding', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ makerAddress: makerAddressLeft, - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2000), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1001), 0), + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(16), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(22), 0), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ makerAddress: makerAddressRight, makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(3000), 0), + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(83), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(49), 0), feeRecipientAddress: feeRecipientAddressRight, }); // Note: + // The correct price buy price for the right maker would yield (49/83) * 22 = 12.988 units + // of the left maker asset. This gets rounded up to 13, giving the right maker a better price. + // Note: // The maker/taker fee percentage paid on the right order differs because - // they received different sale prices. Similarly, the right maker pays a - // slightly higher lower than the right taker. + // they received different sale prices. The right maker pays a + // fee slightly lower than the right taker. const expectedTransferAmounts = { // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2000), 0), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1001), 0), + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(16), 0), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(22), 0), feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1001), 0), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(301), 0), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10.01), 16), // 10.01% + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(22), 0), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber('26.5060240963855421'), 16), // 26.506% // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1699), 0), + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber('10.0333333333333333'), 16), // 10.03% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber('26.5306122448979591'), 16), // 26.531% }; // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndAssertEffectsAsync( @@ -218,7 +361,7 @@ describe('matchOrders', () => { ); }); - it('Should give left maker a better price when correct price is not integral', async () => { + it('Should give left maker a better sell price when rounding', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ makerAddress: makerAddressLeft, @@ -236,7 +379,8 @@ describe('matchOrders', () => { }); // Note: // The maker/taker fee percentage paid on the left order differs because - // they received different sale prices. + // they received different sale prices. The left maker pays a fee + // slightly lower than the left taker. const expectedTransferAmounts = { // Left Maker amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(11), 0), @@ -266,39 +410,45 @@ describe('matchOrders', () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ makerAddress: makerAddressLeft, - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1005), 0), feeRecipientAddress: feeRecipientAddressLeft, }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ makerAddress: makerAddressRight, makerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress), takerAssetData: assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 0), + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2126), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1063), 0), feeRecipientAddress: feeRecipientAddressRight, }); - // TODO: These values will change after implementation of rounding up has been merged const expectedTransferAmounts = { // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 0), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 0), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1005), 0), feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% // Right Maker - // Note: - // For order [4,2] valid fill amounts through `Exchange.fillOrder` would be [2, 1] or [4, 2] - // In this case we have fill amounts of [3, 1] which is attainable through - // `Exchange.matchOrders` but not `Exchange.fillOrder` - // Note: - // The right maker fee differs from the right taker fee because their exchange rate differs. - // The right maker always receives the better exchange and fee price. - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(3), 0), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 0), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 16), // 75% + // Notes: + // i. + // The left order is fully filled by the right order, so the right maker must sell 1005 units of their asset to the left maker. + // By selling 1005 units, the right maker should theoretically receive 502.5 units of the left maker's asset. + // Since the transfer amount must be an integer, this value must be rounded down to 502 or up to 503. + // ii. + // If the right order were filled via `Exchange.fillOrder` the respective fill amounts would be [1004, 502] or [1006, 503]. + // It follows that we cannot trigger a sale of 1005 units of the right maker's asset through `Exchange.fillOrder`. + // iii. + // For an optimal match, the algorithm must choose either [1005, 502] or [1005, 503] as fill amounts for the right order. + // The algorithm favors the right maker when the exchange rate must be rounded, so the final fill for the right order is [1005, 503]. + // iv. + // The right maker fee differs from the right taker fee because their exchange rate differs. + // The right maker always receives the better exchange and fee price. + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(1005), 0), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(503), 0), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber('47.2718720602069614'), 16), // 47.27% // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(8), 0), + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(497), 0), feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber('47.3189087488240827'), 16), // 47.31% }; // Match signedOrderLeft with signedOrderRight await matchOrderTester.matchOrdersAndAssertEffectsAsync( -- cgit From f225f9e7c8f59a0ea04f6c9b07493a0458c4f502 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Tue, 28 Aug 2018 13:25:05 -0700 Subject: Making rounding consistent in calculateFillResults --- .../src/2.0.0/protocol/Exchange/MixinExchangeCore.sol | 4 ++-- packages/contracts/test/exchange/internal.ts | 17 ++++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) (limited to 'packages') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol index 64b1f7665..ff908917d 100644 --- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol @@ -459,8 +459,8 @@ contract MixinExchangeCore is order.makerAssetAmount ); fillResults.makerFeePaid = safeGetPartialAmountFloor( - takerAssetFilledAmount, - order.takerAssetAmount, + fillResults.makerAssetFilledAmount, + order.makerAssetAmount, order.makerFee ); fillResults.takerFeePaid = safeGetPartialAmountFloor( diff --git a/packages/contracts/test/exchange/internal.ts b/packages/contracts/test/exchange/internal.ts index c5d2fc58f..dc2c5fbe0 100644 --- a/packages/contracts/test/exchange/internal.ts +++ b/packages/contracts/test/exchange/internal.ts @@ -221,16 +221,19 @@ describe('Exchange core internal functions', () => { // in any mathematical operation in either the reference TypeScript // implementation or the Solidity implementation of // calculateFillResults. + const makerAssetFilledAmount = await referenceSafeGetPartialAmountFloorAsync( + takerAssetFilledAmount, + orderTakerAssetAmount, + otherAmount, + ); + const order = makeOrder(otherAmount, orderTakerAssetAmount, otherAmount, otherAmount); + const orderMakerAssetAmount = order.makerAssetAmount; return { - makerAssetFilledAmount: await referenceSafeGetPartialAmountFloorAsync( - takerAssetFilledAmount, - orderTakerAssetAmount, - otherAmount, - ), + makerAssetFilledAmount, takerAssetFilledAmount, makerFeePaid: await referenceSafeGetPartialAmountFloorAsync( - takerAssetFilledAmount, - orderTakerAssetAmount, + makerAssetFilledAmount, + orderMakerAssetAmount, otherAmount, ), takerFeePaid: await referenceSafeGetPartialAmountFloorAsync( -- cgit