From e617da3bbf650b25c29df33cd12e23c994efe674 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 6 Jun 2018 22:13:50 +0200 Subject: Implement initial generateOrder function that given the scenario values for an order, generates the actual order. --- .../contracts/src/utils/combinatorial_utils.ts | 213 +++++++++++++++++++++ packages/contracts/src/utils/types.ts | 31 +++ 2 files changed, 244 insertions(+) create mode 100644 packages/contracts/src/utils/combinatorial_utils.ts diff --git a/packages/contracts/src/utils/combinatorial_utils.ts b/packages/contracts/src/utils/combinatorial_utils.ts new file mode 100644 index 000000000..c7d48c86d --- /dev/null +++ b/packages/contracts/src/utils/combinatorial_utils.ts @@ -0,0 +1,213 @@ +import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { Order } from '@0xproject/types'; +import { BigNumber, errorUtils } from '@0xproject/utils'; + +import { constants } from './constants'; +import { + AssetDataScenario, + ERC721Token, + ExpirationTimeSecondsScenario, + FeeRecipientAddressScenario, + OrderAmountScenario, +} from './types'; + +const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10000000000000000000); +const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100000000000000000); +const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1000000); +const ONE_NFT_UNIT = new BigNumber(1); +const TEN_MINUTES_MS = 1000 * 60 * 10; + +export const combinatorialUtils = { + generateOrder( + userAddresses: string[], + zrxAddress: string, + nonZrxERC20EighteenDecimalTokenAddress: string, + erc20FiveDecimalTokenAddress: string, + erc721Token: ERC721Token, + exchangeAddress: string, + feeRecipientScenario: FeeRecipientAddressScenario, + makerAssetAmountScenario: OrderAmountScenario, + takerAssetAmountScenario: OrderAmountScenario, + makerFeeScenario: OrderAmountScenario, + takerFeeScenario: OrderAmountScenario, + expirationTimeSecondsScenario: ExpirationTimeSecondsScenario, + makerAssetDataScenario: AssetDataScenario, + takerAssetDataScenario: AssetDataScenario, + ): Order { + let feeRecipientAddress; + let makerAssetAmount; + let takerAssetAmount; + let makerFee; + let takerFee; + let expirationTimeSeconds; + let makerAssetData; + let takerAssetData; + + switch (feeRecipientScenario) { + case FeeRecipientAddressScenario.BurnAddress: + feeRecipientAddress = constants.NULL_ADDRESS; + break; + case FeeRecipientAddressScenario.EthUserAddress: + feeRecipientAddress = userAddresses[4]; + break; + default: + throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', feeRecipientScenario); + } + + const invalidAssetProxyIdHex = '0A'; + switch (makerAssetDataScenario) { + case AssetDataScenario.ZRXFeeToken: + makerAssetData = assetProxyUtils.encodeERC20ProxyData(zrxAddress); + break; + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + makerAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress); + break; + case AssetDataScenario.ERC20FiveDecimals: + makerAssetData = assetProxyUtils.encodeERC20ProxyData(erc20FiveDecimalTokenAddress); + break; + case AssetDataScenario.ERC20InvalidAssetProxyId: { + const validAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress); + makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; + break; + } + case AssetDataScenario.ERC721ValidAssetProxyId: + makerAssetData = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721Token.id); + break; + case AssetDataScenario.ERC721InvalidAssetProxyId: { + const validAssetData = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721Token.id); + makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; + break; + } + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', makerAssetDataScenario); + } + + switch (takerAssetDataScenario) { + case AssetDataScenario.ZRXFeeToken: + takerAssetData = assetProxyUtils.encodeERC20ProxyData(zrxAddress); + break; + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + takerAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress); + break; + case AssetDataScenario.ERC20FiveDecimals: + takerAssetData = assetProxyUtils.encodeERC20ProxyData(erc20FiveDecimalTokenAddress); + break; + case AssetDataScenario.ERC20InvalidAssetProxyId: { + const validAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress); + takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; + break; + } + case AssetDataScenario.ERC721ValidAssetProxyId: + takerAssetData = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721Token.id); + break; + case AssetDataScenario.ERC721InvalidAssetProxyId: { + const validAssetData = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721Token.id); + takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; + break; + } + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', takerAssetDataScenario); + } + + switch (makerAssetAmountScenario) { + case OrderAmountScenario.NonZero: + switch (makerAssetDataScenario) { + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + case AssetDataScenario.ERC20InvalidAssetProxyId: + makerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; + break; + case AssetDataScenario.ERC20FiveDecimals: + makerAssetAmount = TEN_UNITS_FIVE_DECIMALS; + break; + case AssetDataScenario.ERC721ValidAssetProxyId: + case AssetDataScenario.ERC721InvalidAssetProxyId: + makerAssetAmount = ONE_NFT_UNIT; + break; + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', makerAssetDataScenario); + } + break; + case OrderAmountScenario.Zero: + makerAssetAmount = new BigNumber(0); + break; + default: + throw errorUtils.spawnSwitchErr('OrderAmountScenario', makerAssetAmountScenario); + } + + switch (takerAssetAmountScenario) { + case OrderAmountScenario.NonZero: + switch (takerAssetDataScenario) { + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + case AssetDataScenario.ERC20InvalidAssetProxyId: + takerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; + break; + case AssetDataScenario.ERC20FiveDecimals: + takerAssetAmount = TEN_UNITS_FIVE_DECIMALS; + break; + case AssetDataScenario.ERC721ValidAssetProxyId: + case AssetDataScenario.ERC721InvalidAssetProxyId: + takerAssetAmount = ONE_NFT_UNIT; + break; + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', takerAssetDataScenario); + } + break; + case OrderAmountScenario.Zero: + takerAssetAmount = new BigNumber(0); + break; + default: + throw errorUtils.spawnSwitchErr('OrderAmountScenario', takerAssetAmountScenario); + } + + switch (makerFeeScenario) { + case OrderAmountScenario.NonZero: + makerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; + break; + case OrderAmountScenario.Zero: + makerFee = new BigNumber(0); + break; + default: + throw errorUtils.spawnSwitchErr('OrderAmountScenario', makerFeeScenario); + } + + switch (takerFeeScenario) { + case OrderAmountScenario.NonZero: + takerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; + break; + case OrderAmountScenario.Zero: + takerFee = new BigNumber(0); + break; + default: + throw errorUtils.spawnSwitchErr('OrderAmountScenario', takerFeeScenario); + } + + switch (expirationTimeSecondsScenario) { + case ExpirationTimeSecondsScenario.InFuture: + expirationTimeSeconds = new BigNumber(Date.now() + TEN_MINUTES_MS); + break; + case ExpirationTimeSecondsScenario.InPast: + expirationTimeSeconds = new BigNumber(Date.now() - TEN_MINUTES_MS); + break; + default: + throw errorUtils.spawnSwitchErr('ExpirationTimeSecondsScenario', expirationTimeSecondsScenario); + } + + const order: Order = { + senderAddress: constants.NULL_ADDRESS, + makerAddress: userAddresses[1], + takerAddress: userAddresses[2], + makerFee, + takerFee, + makerAssetAmount, + takerAssetAmount, + makerAssetData, + takerAssetData, + salt: generatePseudoRandomSalt(), + exchangeAddress, + feeRecipientAddress, + expirationTimeSeconds, + }; + + return order; + }, +}; diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 360e1fdbc..58d855c04 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -147,3 +147,34 @@ export interface MatchOrder { leftSignature: string; rightSignature: string; } + +export interface ERC721Token { + address: string; + id: BigNumber; +} + +// Combinatorial testing types + +export enum FeeRecipientAddressScenario { + BurnAddress = 'BURN_ADDRESS', + EthUserAddress = 'ETH_USER_ADDRESS', +} + +export enum OrderAmountScenario { + Zero = 'ZERO', + NonZero = 'NON_ZERO', +} + +export enum ExpirationTimeSecondsScenario { + InPast = 'IN_PAST', + InFuture = 'IN_FUTURE', +} + +export enum AssetDataScenario { + ERC721ValidAssetProxyId = 'ERC721_VALID_ASSET_PROXY_ID', + ERC721InvalidAssetProxyId = 'ERC721_INVALID_ASSET_PROXY_ID', + ZRXFeeToken = 'ZRX_FEE_TOKEN', + ERC20InvalidAssetProxyId = 'ERC20_INVALID_ASSET_PROXY_ID', + ERC20FiveDecimals = 'ERC20_FIVE_DECIMALS', + ERC20NonZRXEighteenDecimals = 'ERC20_NON_ZRX_EIGHTEEN_DECIMALS', +} -- cgit From ab5e021bda5cc8e39d8595580c09c3540a09aff5 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 7 Jun 2018 18:00:13 +0200 Subject: POC: Generates an order from spec, get's the amount fillable --- .../contracts/src/utils/combinatorial_utils.ts | 213 ----------------- packages/contracts/src/utils/erc20_wrapper.ts | 28 ++- packages/contracts/src/utils/new_order_factory.ts | 264 ++++++++++++++++++++ packages/contracts/src/utils/order_info_utils.ts | 44 ++++ .../simple_erc20_balance_and_allowance_fetcher.ts | 20 ++ .../src/utils/simple_filled_cancelled_fetcher.ts | 32 +++ packages/contracts/src/utils/types.ts | 5 - packages/contracts/test/combinatorial_tests.ts | 266 +++++++++++++++++++++ 8 files changed, 651 insertions(+), 221 deletions(-) delete mode 100644 packages/contracts/src/utils/combinatorial_utils.ts create mode 100644 packages/contracts/src/utils/new_order_factory.ts create mode 100644 packages/contracts/src/utils/order_info_utils.ts create mode 100644 packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts create mode 100644 packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts create mode 100644 packages/contracts/test/combinatorial_tests.ts diff --git a/packages/contracts/src/utils/combinatorial_utils.ts b/packages/contracts/src/utils/combinatorial_utils.ts deleted file mode 100644 index c7d48c86d..000000000 --- a/packages/contracts/src/utils/combinatorial_utils.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; -import { Order } from '@0xproject/types'; -import { BigNumber, errorUtils } from '@0xproject/utils'; - -import { constants } from './constants'; -import { - AssetDataScenario, - ERC721Token, - ExpirationTimeSecondsScenario, - FeeRecipientAddressScenario, - OrderAmountScenario, -} from './types'; - -const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10000000000000000000); -const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100000000000000000); -const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1000000); -const ONE_NFT_UNIT = new BigNumber(1); -const TEN_MINUTES_MS = 1000 * 60 * 10; - -export const combinatorialUtils = { - generateOrder( - userAddresses: string[], - zrxAddress: string, - nonZrxERC20EighteenDecimalTokenAddress: string, - erc20FiveDecimalTokenAddress: string, - erc721Token: ERC721Token, - exchangeAddress: string, - feeRecipientScenario: FeeRecipientAddressScenario, - makerAssetAmountScenario: OrderAmountScenario, - takerAssetAmountScenario: OrderAmountScenario, - makerFeeScenario: OrderAmountScenario, - takerFeeScenario: OrderAmountScenario, - expirationTimeSecondsScenario: ExpirationTimeSecondsScenario, - makerAssetDataScenario: AssetDataScenario, - takerAssetDataScenario: AssetDataScenario, - ): Order { - let feeRecipientAddress; - let makerAssetAmount; - let takerAssetAmount; - let makerFee; - let takerFee; - let expirationTimeSeconds; - let makerAssetData; - let takerAssetData; - - switch (feeRecipientScenario) { - case FeeRecipientAddressScenario.BurnAddress: - feeRecipientAddress = constants.NULL_ADDRESS; - break; - case FeeRecipientAddressScenario.EthUserAddress: - feeRecipientAddress = userAddresses[4]; - break; - default: - throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', feeRecipientScenario); - } - - const invalidAssetProxyIdHex = '0A'; - switch (makerAssetDataScenario) { - case AssetDataScenario.ZRXFeeToken: - makerAssetData = assetProxyUtils.encodeERC20ProxyData(zrxAddress); - break; - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - makerAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress); - break; - case AssetDataScenario.ERC20FiveDecimals: - makerAssetData = assetProxyUtils.encodeERC20ProxyData(erc20FiveDecimalTokenAddress); - break; - case AssetDataScenario.ERC20InvalidAssetProxyId: { - const validAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress); - makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; - break; - } - case AssetDataScenario.ERC721ValidAssetProxyId: - makerAssetData = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721Token.id); - break; - case AssetDataScenario.ERC721InvalidAssetProxyId: { - const validAssetData = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721Token.id); - makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; - break; - } - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', makerAssetDataScenario); - } - - switch (takerAssetDataScenario) { - case AssetDataScenario.ZRXFeeToken: - takerAssetData = assetProxyUtils.encodeERC20ProxyData(zrxAddress); - break; - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - takerAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress); - break; - case AssetDataScenario.ERC20FiveDecimals: - takerAssetData = assetProxyUtils.encodeERC20ProxyData(erc20FiveDecimalTokenAddress); - break; - case AssetDataScenario.ERC20InvalidAssetProxyId: { - const validAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress); - takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; - break; - } - case AssetDataScenario.ERC721ValidAssetProxyId: - takerAssetData = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721Token.id); - break; - case AssetDataScenario.ERC721InvalidAssetProxyId: { - const validAssetData = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721Token.id); - takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; - break; - } - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', takerAssetDataScenario); - } - - switch (makerAssetAmountScenario) { - case OrderAmountScenario.NonZero: - switch (makerAssetDataScenario) { - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - case AssetDataScenario.ERC20InvalidAssetProxyId: - makerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; - break; - case AssetDataScenario.ERC20FiveDecimals: - makerAssetAmount = TEN_UNITS_FIVE_DECIMALS; - break; - case AssetDataScenario.ERC721ValidAssetProxyId: - case AssetDataScenario.ERC721InvalidAssetProxyId: - makerAssetAmount = ONE_NFT_UNIT; - break; - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', makerAssetDataScenario); - } - break; - case OrderAmountScenario.Zero: - makerAssetAmount = new BigNumber(0); - break; - default: - throw errorUtils.spawnSwitchErr('OrderAmountScenario', makerAssetAmountScenario); - } - - switch (takerAssetAmountScenario) { - case OrderAmountScenario.NonZero: - switch (takerAssetDataScenario) { - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - case AssetDataScenario.ERC20InvalidAssetProxyId: - takerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; - break; - case AssetDataScenario.ERC20FiveDecimals: - takerAssetAmount = TEN_UNITS_FIVE_DECIMALS; - break; - case AssetDataScenario.ERC721ValidAssetProxyId: - case AssetDataScenario.ERC721InvalidAssetProxyId: - takerAssetAmount = ONE_NFT_UNIT; - break; - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', takerAssetDataScenario); - } - break; - case OrderAmountScenario.Zero: - takerAssetAmount = new BigNumber(0); - break; - default: - throw errorUtils.spawnSwitchErr('OrderAmountScenario', takerAssetAmountScenario); - } - - switch (makerFeeScenario) { - case OrderAmountScenario.NonZero: - makerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; - break; - case OrderAmountScenario.Zero: - makerFee = new BigNumber(0); - break; - default: - throw errorUtils.spawnSwitchErr('OrderAmountScenario', makerFeeScenario); - } - - switch (takerFeeScenario) { - case OrderAmountScenario.NonZero: - takerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; - break; - case OrderAmountScenario.Zero: - takerFee = new BigNumber(0); - break; - default: - throw errorUtils.spawnSwitchErr('OrderAmountScenario', takerFeeScenario); - } - - switch (expirationTimeSecondsScenario) { - case ExpirationTimeSecondsScenario.InFuture: - expirationTimeSeconds = new BigNumber(Date.now() + TEN_MINUTES_MS); - break; - case ExpirationTimeSecondsScenario.InPast: - expirationTimeSeconds = new BigNumber(Date.now() - TEN_MINUTES_MS); - break; - default: - throw errorUtils.spawnSwitchErr('ExpirationTimeSecondsScenario', expirationTimeSecondsScenario); - } - - const order: Order = { - senderAddress: constants.NULL_ADDRESS, - makerAddress: userAddresses[1], - takerAddress: userAddresses[2], - makerFee, - takerFee, - makerAssetAmount, - takerAssetAmount, - makerAssetData, - takerAssetData, - salt: generatePseudoRandomSalt(), - exchangeAddress, - feeRecipientAddress, - expirationTimeSeconds, - }; - - return order; - }, -}; diff --git a/packages/contracts/src/utils/erc20_wrapper.ts b/packages/contracts/src/utils/erc20_wrapper.ts index dceeceeea..efb245c89 100644 --- a/packages/contracts/src/utils/erc20_wrapper.ts +++ b/packages/contracts/src/utils/erc20_wrapper.ts @@ -25,8 +25,11 @@ export class ERC20Wrapper { this._tokenOwnerAddresses = tokenOwnerAddresses; this._contractOwnerAddress = contractOwnerAddress; } - public async deployDummyTokensAsync(): Promise { - for (let i = 0; i < constants.NUM_DUMMY_ERC20_TO_DEPLOY; i++) { + public async deployDummyTokensAsync(num?: number, decimals?: BigNumber): Promise { + // TODO(fabio): Remove and refactor all tests + const finalNum = _.isUndefined(num) ? constants.NUM_DUMMY_ERC20_TO_DEPLOY : num; + const finalDecimals = _.isUndefined(decimals) ? constants.DUMMY_TOKEN_DECIMALS : decimals; + for (let i = 0; i < finalNum; i++) { this._dummyTokenContracts.push( await DummyERC20TokenContract.deployFrom0xArtifactAsync( artifacts.DummyERC20Token, @@ -34,7 +37,7 @@ export class ERC20Wrapper { txDefaults, constants.DUMMY_TOKEN_NAME, constants.DUMMY_TOKEN_SYMBOL, - constants.DUMMY_TOKEN_DECIMALS, + finalDecimals, constants.DUMMY_TOKEN_TOTAL_SUPPLY, ), ); @@ -73,6 +76,25 @@ export class ERC20Wrapper { } } } + public async getBalanceAsync(owner: string, token: string): Promise { + const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === token); + if (_.isUndefined(tokenContractIfExists)) { + throw new Error(`Token: ${token} was not deployed through ERC20Wrapper`); + } + const balance = new BigNumber(await tokenContractIfExists.balanceOf.callAsync(owner)); + return balance; + } + public async getProxyAllowanceAsync(owner: string, token: string): Promise { + this._validateProxyContractExistsOrThrow(); + const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === token); + if (_.isUndefined(tokenContractIfExists)) { + throw new Error(`Token: ${token} was not deployed through ERC20Wrapper`); + } + const balance = new BigNumber( + await tokenContractIfExists.allowance.callAsync(owner, (this._proxyContract as ERC20ProxyContract).address), + ); + return balance; + } public async getBalancesAsync(): Promise { this._validateDummyTokenContractsExistOrThrow(); const balancesByOwner: ERC20BalancesByOwner = {}; diff --git a/packages/contracts/src/utils/new_order_factory.ts b/packages/contracts/src/utils/new_order_factory.ts new file mode 100644 index 000000000..a4ded4230 --- /dev/null +++ b/packages/contracts/src/utils/new_order_factory.ts @@ -0,0 +1,264 @@ +import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { Order } from '@0xproject/types'; +import { BigNumber, errorUtils } from '@0xproject/utils'; + +import { DummyERC721TokenContract } from '../contract_wrappers/generated/dummy_e_r_c721_token'; + +import { constants } from './constants'; +import { + AssetDataScenario, + ERC721TokenIdsByOwner, + ExpirationTimeSecondsScenario, + FeeRecipientAddressScenario, + OrderAmountScenario, +} from './types'; + +const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10000000000000000000); +const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100000000000000000); +const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1000000); +const ONE_NFT_UNIT = new BigNumber(1); +const TEN_MINUTES_MS = 1000 * 60 * 10; + +/* + * TODO: + * - Write function that given an order, fillAmount, retrieves orderRelevantState and maps it to expected test outcome. + * - Write function that generates order permutations. + * - Write functions for other steps that must be permutated + */ + +export class NewOrderFactory { + private _userAddresses: string[]; + private _zrxAddress: string; + private _nonZrxERC20EighteenDecimalTokenAddresses: string[]; + private _erc20FiveDecimalTokenAddresses: string[]; + private _erc721Token: DummyERC721TokenContract; + private _erc721Balances: ERC721TokenIdsByOwner; + private _exchangeAddress: string; + constructor( + userAddresses: string[], + zrxAddress: string, + nonZrxERC20EighteenDecimalTokenAddresses: string[], + erc20FiveDecimalTokenAddresses: string[], + erc721Token: DummyERC721TokenContract, + erc721Balances: ERC721TokenIdsByOwner, + exchangeAddress: string, + ) { + this._userAddresses = userAddresses; + this._zrxAddress = zrxAddress; + this._nonZrxERC20EighteenDecimalTokenAddresses = nonZrxERC20EighteenDecimalTokenAddresses; + this._erc20FiveDecimalTokenAddresses = erc20FiveDecimalTokenAddresses; + this._erc721Token = erc721Token; + this._erc721Balances = erc721Balances; + this._exchangeAddress = exchangeAddress; + } + public generateOrder( + feeRecipientScenario: FeeRecipientAddressScenario, + makerAssetAmountScenario: OrderAmountScenario, + takerAssetAmountScenario: OrderAmountScenario, + makerFeeScenario: OrderAmountScenario, + takerFeeScenario: OrderAmountScenario, + expirationTimeSecondsScenario: ExpirationTimeSecondsScenario, + makerAssetDataScenario: AssetDataScenario, + takerAssetDataScenario: AssetDataScenario, + ): Order { + const makerAddress = this._userAddresses[1]; + const takerAddress = this._userAddresses[2]; + const erc721MakerAssetIds = this._erc721Balances[makerAddress][this._erc721Token.address]; + const erc721TakerAssetIds = this._erc721Balances[takerAddress][this._erc721Token.address]; + let feeRecipientAddress; + let makerAssetAmount; + let takerAssetAmount; + let makerFee; + let takerFee; + let expirationTimeSeconds; + let makerAssetData; + let takerAssetData; + + switch (feeRecipientScenario) { + case FeeRecipientAddressScenario.BurnAddress: + feeRecipientAddress = constants.NULL_ADDRESS; + break; + case FeeRecipientAddressScenario.EthUserAddress: + feeRecipientAddress = this._userAddresses[4]; + break; + default: + throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', feeRecipientScenario); + } + + const invalidAssetProxyIdHex = '0A'; + switch (makerAssetDataScenario) { + case AssetDataScenario.ZRXFeeToken: + makerAssetData = assetProxyUtils.encodeERC20ProxyData(this._zrxAddress); + break; + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + makerAssetData = assetProxyUtils.encodeERC20ProxyData( + this._nonZrxERC20EighteenDecimalTokenAddresses[0], + ); + break; + case AssetDataScenario.ERC20FiveDecimals: + makerAssetData = assetProxyUtils.encodeERC20ProxyData(this._erc20FiveDecimalTokenAddresses[0]); + break; + case AssetDataScenario.ERC20InvalidAssetProxyId: { + const validAssetData = assetProxyUtils.encodeERC20ProxyData( + this._nonZrxERC20EighteenDecimalTokenAddresses[0], + ); + makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; + break; + } + case AssetDataScenario.ERC721ValidAssetProxyId: + makerAssetData = assetProxyUtils.encodeERC721ProxyData( + this._erc721Token.address, + erc721MakerAssetIds[0], + ); + break; + case AssetDataScenario.ERC721InvalidAssetProxyId: { + const validAssetData = assetProxyUtils.encodeERC721ProxyData( + this._erc721Token.address, + erc721MakerAssetIds[0], + ); + makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; + break; + } + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', makerAssetDataScenario); + } + + switch (takerAssetDataScenario) { + case AssetDataScenario.ZRXFeeToken: + takerAssetData = assetProxyUtils.encodeERC20ProxyData(this._zrxAddress); + break; + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + takerAssetData = assetProxyUtils.encodeERC20ProxyData( + this._nonZrxERC20EighteenDecimalTokenAddresses[1], + ); + break; + case AssetDataScenario.ERC20FiveDecimals: + takerAssetData = assetProxyUtils.encodeERC20ProxyData(this._erc20FiveDecimalTokenAddresses[1]); + break; + case AssetDataScenario.ERC20InvalidAssetProxyId: { + const validAssetData = assetProxyUtils.encodeERC20ProxyData( + this._nonZrxERC20EighteenDecimalTokenAddresses[1], + ); + takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; + break; + } + case AssetDataScenario.ERC721ValidAssetProxyId: + takerAssetData = assetProxyUtils.encodeERC721ProxyData( + this._erc721Token.address, + erc721TakerAssetIds[0], + ); + break; + case AssetDataScenario.ERC721InvalidAssetProxyId: { + const validAssetData = assetProxyUtils.encodeERC721ProxyData( + this._erc721Token.address, + erc721TakerAssetIds[0], + ); + takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; + break; + } + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', takerAssetDataScenario); + } + + switch (makerAssetAmountScenario) { + case OrderAmountScenario.NonZero: + switch (makerAssetDataScenario) { + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + case AssetDataScenario.ERC20InvalidAssetProxyId: + makerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; + break; + case AssetDataScenario.ERC20FiveDecimals: + makerAssetAmount = TEN_UNITS_FIVE_DECIMALS; + break; + case AssetDataScenario.ERC721ValidAssetProxyId: + case AssetDataScenario.ERC721InvalidAssetProxyId: + makerAssetAmount = ONE_NFT_UNIT; + break; + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', makerAssetDataScenario); + } + break; + case OrderAmountScenario.Zero: + makerAssetAmount = new BigNumber(0); + break; + default: + throw errorUtils.spawnSwitchErr('OrderAmountScenario', makerAssetAmountScenario); + } + + switch (takerAssetAmountScenario) { + case OrderAmountScenario.NonZero: + switch (takerAssetDataScenario) { + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + case AssetDataScenario.ERC20InvalidAssetProxyId: + takerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; + break; + case AssetDataScenario.ERC20FiveDecimals: + takerAssetAmount = TEN_UNITS_FIVE_DECIMALS; + break; + case AssetDataScenario.ERC721ValidAssetProxyId: + case AssetDataScenario.ERC721InvalidAssetProxyId: + takerAssetAmount = ONE_NFT_UNIT; + break; + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', takerAssetDataScenario); + } + break; + case OrderAmountScenario.Zero: + takerAssetAmount = new BigNumber(0); + break; + default: + throw errorUtils.spawnSwitchErr('OrderAmountScenario', takerAssetAmountScenario); + } + + switch (makerFeeScenario) { + case OrderAmountScenario.NonZero: + makerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; + break; + case OrderAmountScenario.Zero: + makerFee = new BigNumber(0); + break; + default: + throw errorUtils.spawnSwitchErr('OrderAmountScenario', makerFeeScenario); + } + + switch (takerFeeScenario) { + case OrderAmountScenario.NonZero: + takerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; + break; + case OrderAmountScenario.Zero: + takerFee = new BigNumber(0); + break; + default: + throw errorUtils.spawnSwitchErr('OrderAmountScenario', takerFeeScenario); + } + + switch (expirationTimeSecondsScenario) { + case ExpirationTimeSecondsScenario.InFuture: + expirationTimeSeconds = new BigNumber(Date.now() + TEN_MINUTES_MS); + break; + case ExpirationTimeSecondsScenario.InPast: + expirationTimeSeconds = new BigNumber(Date.now() - TEN_MINUTES_MS); + break; + default: + throw errorUtils.spawnSwitchErr('ExpirationTimeSecondsScenario', expirationTimeSecondsScenario); + } + + const order: Order = { + senderAddress: constants.NULL_ADDRESS, + makerAddress, + takerAddress, + makerFee, + takerFee, + makerAssetAmount, + takerAssetAmount, + makerAssetData, + takerAssetData, + salt: generatePseudoRandomSalt(), + exchangeAddress: this._exchangeAddress, + feeRecipientAddress, + expirationTimeSeconds, + }; + + return order; + } +} diff --git a/packages/contracts/src/utils/order_info_utils.ts b/packages/contracts/src/utils/order_info_utils.ts new file mode 100644 index 000000000..9df627da3 --- /dev/null +++ b/packages/contracts/src/utils/order_info_utils.ts @@ -0,0 +1,44 @@ +import { assetProxyUtils, OrderStateUtils } from '@0xproject/order-utils'; +import { SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; + +import { ExchangeContract } from '../contract_wrappers/generated/exchange'; + +import { constants } from './constants'; +import { ERC20Wrapper } from './erc20_wrapper'; +import { SimpleERC20BalanceAndProxyAllowanceFetcher } from './simple_erc20_balance_and_allowance_fetcher'; +import { SimpleOrderFilledCancelledFetcher } from './simple_filled_cancelled_fetcher'; + +export class OrderInfoUtils { + private _orderStateUtils: OrderStateUtils; + private _erc20Wrapper: ERC20Wrapper; + constructor(exchangeContract: ExchangeContract, erc20Wrapper: ERC20Wrapper, zrxAddress: string) { + this._erc20Wrapper = erc20Wrapper; + const simpleOrderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher(exchangeContract, zrxAddress); + const simpleERC20BalanceAndProxyAllowanceFetcher = new SimpleERC20BalanceAndProxyAllowanceFetcher(erc20Wrapper); + this._orderStateUtils = new OrderStateUtils( + simpleERC20BalanceAndProxyAllowanceFetcher, + simpleOrderFilledCancelledFetcher, + ); + } + public async getFillableTakerAssetAmountAsync(signedOrder: SignedOrder, takerAddress: string): Promise { + const orderRelevantState = await this._orderStateUtils.getOrderRelevantStateAsync(signedOrder); + console.log('orderRelevantState', orderRelevantState); + if (takerAddress === constants.NULL_ADDRESS) { + return orderRelevantState.remainingFillableTakerAssetAmount; + } + const takerAssetData = assetProxyUtils.decodeERC20ProxyData(signedOrder.takerAssetData); + const takerBalance = await this._erc20Wrapper.getBalanceAsync(takerAddress, takerAssetData.tokenAddress); + const takerAllowance = await this._erc20Wrapper.getProxyAllowanceAsync( + takerAddress, + takerAssetData.tokenAddress, + ); + // TODO: We also need to make sure taker has sufficient ZRX for fees... + const fillableTakerAssetAmount = BigNumber.min([ + takerBalance, + takerAllowance, + orderRelevantState.remainingFillableTakerAssetAmount, + ]); + return fillableTakerAssetAmount; + } +} diff --git a/packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts b/packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts new file mode 100644 index 000000000..6eed9227a --- /dev/null +++ b/packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts @@ -0,0 +1,20 @@ +import { AbstractBalanceAndProxyAllowanceFetcher } from '@0xproject/order-utils'; +import { BigNumber } from '@0xproject/utils'; + +import { ERC20Wrapper } from './erc20_wrapper'; + +// TODO(fabio): Refactor this to also work for ERC721! +export class SimpleERC20BalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher { + private _erc20TokenWrapper: ERC20Wrapper; + constructor(erc20TokenWrapper: ERC20Wrapper) { + this._erc20TokenWrapper = erc20TokenWrapper; + } + public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise { + const balance = await this._erc20TokenWrapper.getBalanceAsync(userAddress, tokenAddress); + return balance; + } + public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise { + const proxyAllowance = await this._erc20TokenWrapper.getProxyAllowanceAsync(userAddress, tokenAddress); + return proxyAllowance; + } +} diff --git a/packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts b/packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts new file mode 100644 index 000000000..0a80637cc --- /dev/null +++ b/packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts @@ -0,0 +1,32 @@ +import { AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils'; +import { BigNumber } from '@0xproject/utils'; +import { BlockParamLiteral } from 'ethereum-types'; + +import { + CancelContractEventArgs, + ExchangeContract, + FillContractEventArgs, +} from '../contract_wrappers/generated/exchange'; + +export class SimpleOrderFilledCancelledFetcher implements AbstractOrderFilledCancelledFetcher { + private _exchangeContract: ExchangeContract; + private _zrxAddress: string; + constructor(exchange: ExchangeContract, zrxAddress: string) { + this._exchangeContract = exchange; + this._zrxAddress = zrxAddress; + } + public async getFilledTakerAmountAsync(orderHash: string): Promise { + const filledTakerAmount = new BigNumber(await this._exchangeContract.filled.callAsync(orderHash)); + return filledTakerAmount; + } + public async isOrderCancelledAsync(orderHash: string): Promise { + const methodOpts = { + defaultBlock: BlockParamLiteral.Latest, + }; + const isCancelled = await this._exchangeContract.cancelled.callAsync(orderHash); + return isCancelled; + } + public getZRXTokenAddress(): string { + return this._zrxAddress; + } +} diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 94c7f627f..64f6400c5 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -148,11 +148,6 @@ export interface MatchOrder { rightSignature: string; } -export interface ERC721Token { - address: string; - id: BigNumber; -} - // Combinatorial testing types export enum FeeRecipientAddressScenario { diff --git a/packages/contracts/test/combinatorial_tests.ts b/packages/contracts/test/combinatorial_tests.ts new file mode 100644 index 000000000..7a118f6ac --- /dev/null +++ b/packages/contracts/test/combinatorial_tests.ts @@ -0,0 +1,266 @@ +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { assetProxyUtils, crypto, orderHashUtils, OrderStateUtils } from '@0xproject/order-utils'; +import { AssetProxyId, SignatureType, SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import * as chai from 'chai'; +import { LogWithDecodedArgs } from 'ethereum-types'; +import ethUtil = require('ethereumjs-util'); +import 'make-promises-safe'; + +import { DummyERC20TokenContract } from '../src/contract_wrappers/generated/dummy_e_r_c20_token'; +import { DummyERC721TokenContract } from '../src/contract_wrappers/generated/dummy_e_r_c721_token'; +import { ERC20ProxyContract } from '../src/contract_wrappers/generated/e_r_c20_proxy'; +import { ERC721ProxyContract } from '../src/contract_wrappers/generated/e_r_c721_proxy'; +import { + CancelContractEventArgs, + ExchangeContract, + FillContractEventArgs, +} from '../src/contract_wrappers/generated/exchange'; +import { artifacts } from '../src/utils/artifacts'; +import { chaiSetup } from '../src/utils/chai_setup'; +import { constants } from '../src/utils/constants'; +import { ERC20Wrapper } from '../src/utils/erc20_wrapper'; +import { ERC721Wrapper } from '../src/utils/erc721_wrapper'; +import { ExchangeWrapper } from '../src/utils/exchange_wrapper'; +import { NewOrderFactory } from '../src/utils/new_order_factory'; +import { OrderInfoUtils } from '../src/utils/order_info_utils'; +import { orderUtils } from '../src/utils/order_utils'; +import { signingUtils } from '../src/utils/signing_utils'; +import { + AssetDataScenario, + ContractName, + ERC20BalancesByOwner, + ExpirationTimeSecondsScenario, + FeeRecipientAddressScenario, + OrderAmountScenario, + OrderStatus, +} from '../src/utils/types'; + +import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('Combinatorial tests', () => { + let newOrderFactory: NewOrderFactory; + let usedAddresses: string[]; + + let makerAddress: string; + let owner: string; + let takerAddress: string; + let feeRecipientAddress: string; + + let erc20EighteenDecimalTokenA: DummyERC20TokenContract; + let erc20EighteenDecimalTokenB: DummyERC20TokenContract; + let erc20FiveDecimalTokenA: DummyERC20TokenContract; + let erc20FiveDecimalTokenB: DummyERC20TokenContract; + let zrxToken: DummyERC20TokenContract; + let erc721Token: DummyERC721TokenContract; + let exchange: ExchangeContract; + let erc20Proxy: ERC20ProxyContract; + let erc721Proxy: ERC721ProxyContract; + + let erc20Balances: ERC20BalancesByOwner; + let exchangeWrapper: ExchangeWrapper; + let erc20Wrapper: ERC20Wrapper; + let erc721Wrapper: ERC721Wrapper; + + let erc721MakerAssetIds: BigNumber[]; + let erc721TakerAssetIds: BigNumber[]; + + let defaultMakerAssetAddress: string; + let defaultTakerAssetAddress: string; + + before(async () => { + await blockchainLifecycle.startAsync(); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + before(async () => { + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + usedAddresses = [owner, makerAddress, takerAddress, feeRecipientAddress] = accounts; + + erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); + erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); + + const erc20EighteenDecimalTokenCount = 3; + const eighteenDecimals = new BigNumber(18); + [erc20EighteenDecimalTokenA, erc20EighteenDecimalTokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( + erc20EighteenDecimalTokenCount, + eighteenDecimals, + ); + + const erc20FiveDecimalTokenCount = 2; + const fiveDecimals = new BigNumber(18); + [erc20FiveDecimalTokenA, erc20FiveDecimalTokenB] = await erc20Wrapper.deployDummyTokensAsync( + erc20FiveDecimalTokenCount, + fiveDecimals, + ); + erc20Proxy = await erc20Wrapper.deployProxyAsync(); + await erc20Wrapper.setBalancesAndAllowancesAsync(); + + [erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); + erc721Proxy = await erc721Wrapper.deployProxyAsync(); + await erc721Wrapper.setBalancesAndAllowancesAsync(); + const erc721Balances = await erc721Wrapper.getBalancesAsync(); + erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address]; + erc721TakerAssetIds = erc721Balances[takerAddress][erc721Token.address]; + + exchange = await ExchangeContract.deployFrom0xArtifactAsync( + artifacts.Exchange, + provider, + txDefaults, + assetProxyUtils.encodeERC20ProxyData(zrxToken.address), + ); + exchangeWrapper = new ExchangeWrapper(exchange, provider); + await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner); + await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC721, erc721Proxy.address, owner); + + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + defaultMakerAssetAddress = erc20EighteenDecimalTokenA.address; + defaultTakerAssetAddress = erc20EighteenDecimalTokenB.address; + + newOrderFactory = new NewOrderFactory( + usedAddresses, + zrxToken.address, + [erc20EighteenDecimalTokenA.address, erc20EighteenDecimalTokenB.address], + [erc20FiveDecimalTokenA.address, erc20FiveDecimalTokenB.address], + erc721Token, + erc721Balances, + exchange.address, + ); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe.only('Fill order', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + }); + it('should transfer the correct amounts when makerAssetAmount === takerAssetAmount', async () => { + const order = newOrderFactory.generateOrder( + FeeRecipientAddressScenario.EthUserAddress, + OrderAmountScenario.NonZero, + OrderAmountScenario.NonZero, + OrderAmountScenario.Zero, + OrderAmountScenario.Zero, + ExpirationTimeSecondsScenario.InFuture, + AssetDataScenario.ERC20NonZRXEighteenDecimals, + AssetDataScenario.ERC20NonZRXEighteenDecimals, + ); + + // TODO: Permute signature types + + // TODO: Sign order (for now simply ECSign) + const orderHashBuff = orderHashUtils.getOrderHashBuff(order); + const privateKey = constants.TESTRPC_PRIVATE_KEYS[usedAddresses.indexOf(makerAddress)]; + const signature = signingUtils.signMessage(orderHashBuff, privateKey, SignatureType.EthSign); + const signedOrder = { + ...order, + signature: `0x${signature.toString('hex')}`, + }; + console.log('signedOrder', signedOrder); + + // TODO: Get orderRelevantState + const orderInfoUtils = new OrderInfoUtils(exchange, erc20Wrapper, zrxToken.address); + // 1. How much of this order can I fill? + const fillableTakerAssetAmount = await orderInfoUtils.getFillableTakerAssetAmountAsync( + signedOrder, + takerAddress, + ); + console.log('fillableTakerAssetAmount', fillableTakerAssetAmount); + + // TODO: Decide how much to fill (all, some) + const takerFillAmount = fillableTakerAssetAmount.div(2); // some for now + + // 2. If I fill it by X, what are the resulting balances/allowances/filled amounts expected? + // NOTE: we can't use orderStateUtils for this :( We need to do this ourselves. + + // This doesn't include taker balance/allowance checks... + /* + Inputs: + - signedOrder + - takerAddress + Outputs: + - Check fillable amount + - maker token balance & allowance + - maker ZRX balance & allowance + - taker token balance & allowance + - taker ZRX balance & allowance + Test: + - If fillable >= fillAmount: + - check that filled by fillAmount + - check makerBalance + */ + + // signedOrder = orderFactory.newSignedOrder({ + // makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), + // takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), + // }); ); + // + // const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( + // orderHashUtils.getOrderHashHex(signedOrder), + // ); + // expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); + // + // const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); + // await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); + // + // const makerAmountBoughtAfter = await exchangeWrapper.getTakerAssetFilledAmountAsync( + // orderHashUtils.getOrderHashHex(signedOrder), + // ); + // expect(makerAmountBoughtAfter).to.be.bignumber.equal(takerAssetFillAmount); + // + // const newBalances = await erc20Wrapper.getBalancesAsync(); + // + // const makerAssetFilledAmount = takerAssetFillAmount + // .times(signedOrder.makerAssetAmount) + // .dividedToIntegerBy(signedOrder.takerAssetAmount); + // const makerFeePaid = signedOrder.makerFee + // .times(makerAssetFilledAmount) + // .dividedToIntegerBy(signedOrder.makerAssetAmount); + // const takerFeePaid = signedOrder.takerFee + // .times(makerAssetFilledAmount) + // .dividedToIntegerBy(signedOrder.makerAssetAmount); + // expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + // erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount), + // ); + // expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + // erc20Balances[makerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), + // ); + // expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( + // erc20Balances[makerAddress][zrxToken.address].minus(makerFeePaid), + // ); + // expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + // erc20Balances[takerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), + // ); + // expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + // erc20Balances[takerAddress][defaultMakerAssetAddress].add(makerAssetFilledAmount), + // ); + // expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( + // erc20Balances[takerAddress][zrxToken.address].minus(takerFeePaid), + // ); + // expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + // erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), + // ); + }); + }); +}); -- cgit From 78dcb87a7501e9076570b3ed9de2a43b924299f3 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 13 Jun 2018 13:11:45 +0200 Subject: Variable rename for clarity --- packages/contracts/src/utils/exchange_wrapper.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts/src/utils/exchange_wrapper.ts b/packages/contracts/src/utils/exchange_wrapper.ts index a8ca5183e..1ecabcf64 100644 --- a/packages/contracts/src/utils/exchange_wrapper.ts +++ b/packages/contracts/src/utils/exchange_wrapper.ts @@ -33,8 +33,8 @@ export class ExchangeWrapper { params.signature, { from }, ); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; + const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); + return txReceipt; } public async cancelOrderAsync(signedOrder: SignedOrder, from: string): Promise { const params = orderUtils.createCancel(signedOrder); -- cgit From 38de1c9a315aec7f43b08d372dd2360c09bd0acd Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 13 Jun 2018 13:16:22 +0200 Subject: Remove comment since we have added the tests --- packages/order-utils/src/order_validation_utils.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/order-utils/src/order_validation_utils.ts b/packages/order-utils/src/order_validation_utils.ts index 3a6704f26..ab471b455 100644 --- a/packages/order-utils/src/order_validation_utils.ts +++ b/packages/order-utils/src/order_validation_utils.ts @@ -14,11 +14,6 @@ import { utils } from './utils'; export class OrderValidationUtils { private _exchangeContract: ExchangeContract; - // TODO: Write some tests for the function - // 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% public static isRoundingError(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 -- cgit From 3eb232b3fcd73eec25efa6754530bc54b88f3f82 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 13 Jun 2018 13:18:19 +0200 Subject: For some reason order-watcher tests were timeing out so I increased the timeout limit --- packages/order-watcher/test/global_hooks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/order-watcher/test/global_hooks.ts b/packages/order-watcher/test/global_hooks.ts index f18eef379..2018c7545 100644 --- a/packages/order-watcher/test/global_hooks.ts +++ b/packages/order-watcher/test/global_hooks.ts @@ -7,7 +7,7 @@ import { provider } from './utils/web3_wrapper'; before('migrate contracts', async function(): Promise { // HACK: Since the migrations take longer then our global mocha timeout limit // we manually increase it for this before hook. - const mochaTestTimeoutMs = 20000; + const mochaTestTimeoutMs = 25000; this.timeout(mochaTestTimeoutMs); const txDefaults = { gas: devConstants.GAS_LIMIT, -- cgit From 946e6c16442ce434e160fa47a87cd705c5274038 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 13 Jun 2018 13:20:13 +0200 Subject: Remaining refactors of order-utils components for V2 --- .../abstract_order_filled_cancelled_fetcher.ts | 2 +- packages/order-utils/src/index.ts | 1 + packages/order-utils/src/order_validation_utils.ts | 62 +++++++++++----------- .../balance_and_proxy_allowance_lazy_store.ts | 4 +- .../test/exchange_transfer_simulator_test.ts | 25 ++++----- 5 files changed, 48 insertions(+), 46 deletions(-) diff --git a/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts b/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts index ec398a11e..865ea4e43 100644 --- a/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts +++ b/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts @@ -3,5 +3,5 @@ import { BigNumber } from '@0xproject/utils'; export abstract class AbstractOrderFilledCancelledFetcher { public abstract async getFilledTakerAmountAsync(orderHash: string): Promise; public abstract async isOrderCancelledAsync(orderHash: string): Promise; - public abstract getZRXTokenAddress(): string; + public abstract getZRXAssetData(): string; } diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index f9b37df82..592b9b7f6 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -16,6 +16,7 @@ export { generatePseudoRandomSalt } from './salt'; export { OrderError, MessagePrefixType, MessagePrefixOpts } from './types'; export { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; export { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; +export { BalanceAndProxyAllowanceLazyStore } from './store/balance_and_proxy_allowance_lazy_store'; export { RemainingFillableCalculator } from './remaining_fillable_calculator'; export { OrderStateUtils } from './order_state_utils'; export { assetProxyUtils } from './asset_proxy_utils'; diff --git a/packages/order-utils/src/order_validation_utils.ts b/packages/order-utils/src/order_validation_utils.ts index ab471b455..fb96502f2 100644 --- a/packages/order-utils/src/order_validation_utils.ts +++ b/packages/order-utils/src/order_validation_utils.ts @@ -5,15 +5,15 @@ import * as _ from 'lodash'; import { OrderError, TradeSide, TransferType } from './types'; +import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; import { constants } from './constants'; import { ExchangeTransferSimulator } from './exchange_transfer_simulator'; -import { ExchangeContract } from './generated_contract_wrappers/exchange'; import { orderHashUtils } from './order_hash'; import { isValidSignatureAsync } from './signature_utils'; import { utils } from './utils'; export class OrderValidationUtils { - private _exchangeContract: ExchangeContract; + private _orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher; public static isRoundingError(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 @@ -50,12 +50,12 @@ export class OrderValidationUtils { public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync( exchangeTradeEmulator: ExchangeTransferSimulator, signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, + fillTakerAssetAmount: BigNumber, senderAddress: string, - zrxTokenAddress: string, + zrxAssetData: string, ): Promise { const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount( - fillTakerTokenAmount, + fillTakerAssetAmount, signedOrder.takerAssetAmount, signedOrder.makerAssetAmount, ); @@ -71,17 +71,17 @@ export class OrderValidationUtils { signedOrder.takerAssetData, senderAddress, signedOrder.makerAddress, - fillTakerTokenAmount, + fillTakerAssetAmount, TradeSide.Taker, TransferType.Trade, ); const makerFeeAmount = OrderValidationUtils._getPartialAmount( - fillTakerTokenAmount, + fillTakerAssetAmount, signedOrder.takerAssetAmount, signedOrder.makerFee, ); await exchangeTradeEmulator.transferFromAsync( - zrxTokenAddress, + zrxAssetData, signedOrder.makerAddress, signedOrder.feeRecipientAddress, makerFeeAmount, @@ -89,12 +89,12 @@ export class OrderValidationUtils { TransferType.Fee, ); const takerFeeAmount = OrderValidationUtils._getPartialAmount( - fillTakerTokenAmount, + fillTakerAssetAmount, signedOrder.takerAssetAmount, signedOrder.takerFee, ); await exchangeTradeEmulator.transferFromAsync( - zrxTokenAddress, + zrxAssetData, senderAddress, signedOrder.feeRecipientAddress, takerFeeAmount, @@ -123,43 +123,43 @@ export class OrderValidationUtils { .round(0); return fillMakerTokenAmount; } - constructor(exchangeContract: ExchangeContract) { - this._exchangeContract = exchangeContract; + constructor(orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher) { + this._orderFilledCancelledFetcher = orderFilledCancelledFetcher; } public async validateOrderFillableOrThrowAsync( exchangeTradeEmulator: ExchangeTransferSimulator, signedOrder: SignedOrder, - zrxTokenAddress: string, + zrxAssetData: string, expectedFillTakerTokenAmount?: BigNumber, ): Promise { const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - const filledTakerTokenAmount = await this._exchangeContract.filled.callAsync(orderHash); + const filledTakerTokenAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash); OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow( signedOrder.takerAssetAmount, filledTakerTokenAmount, ); OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationTimeSeconds); - let fillTakerTokenAmount = signedOrder.takerAssetAmount.minus(filledTakerTokenAmount); + let fillTakerAssetAmount = signedOrder.takerAssetAmount.minus(filledTakerTokenAmount); if (!_.isUndefined(expectedFillTakerTokenAmount)) { - fillTakerTokenAmount = expectedFillTakerTokenAmount; + fillTakerAssetAmount = expectedFillTakerTokenAmount; } await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( exchangeTradeEmulator, signedOrder, - fillTakerTokenAmount, + fillTakerAssetAmount, signedOrder.takerAddress, - zrxTokenAddress, + zrxAssetData, ); } public async validateFillOrderThrowIfInvalidAsync( exchangeTradeEmulator: ExchangeTransferSimulator, provider: Provider, signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, + fillTakerAssetAmount: BigNumber, takerAddress: string, - zrxTokenAddress: string, + zrxAssetData: string, ): Promise { - if (fillTakerTokenAmount.eq(0)) { + if (fillTakerAssetAmount.eq(0)) { throw new Error(ExchangeContractErrs.OrderFillAmountZero); } const orderHash = orderHashUtils.getOrderHashHex(signedOrder); @@ -172,7 +172,7 @@ export class OrderValidationUtils { if (!isValid) { throw new Error(OrderError.InvalidSignature); } - const filledTakerTokenAmount = await this._exchangeContract.filled.callAsync(orderHash); + const filledTakerTokenAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash); OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow( signedOrder.takerAssetAmount, filledTakerTokenAmount, @@ -182,19 +182,19 @@ export class OrderValidationUtils { } OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationTimeSeconds); const remainingTakerTokenAmount = signedOrder.takerAssetAmount.minus(filledTakerTokenAmount); - const desiredFillTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount) + const desiredFillTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerAssetAmount) ? remainingTakerTokenAmount - : fillTakerTokenAmount; + : fillTakerAssetAmount; await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( exchangeTradeEmulator, signedOrder, desiredFillTakerTokenAmount, takerAddress, - zrxTokenAddress, + zrxAssetData, ); const wouldRoundingErrorOccur = OrderValidationUtils.isRoundingError( - filledTakerTokenAmount, + desiredFillTakerTokenAmount, signedOrder.takerAssetAmount, signedOrder.makerAssetAmount, ); @@ -207,19 +207,19 @@ export class OrderValidationUtils { exchangeTradeEmulator: ExchangeTransferSimulator, provider: Provider, signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, + fillTakerAssetAmount: BigNumber, takerAddress: string, - zrxTokenAddress: string, + zrxAssetData: string, ): Promise { const filledTakerTokenAmount = await this.validateFillOrderThrowIfInvalidAsync( exchangeTradeEmulator, provider, signedOrder, - fillTakerTokenAmount, + fillTakerAssetAmount, takerAddress, - zrxTokenAddress, + zrxAssetData, ); - if (filledTakerTokenAmount !== fillTakerTokenAmount) { + if (filledTakerTokenAmount !== fillTakerAssetAmount) { throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount); } } diff --git a/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts b/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts index 08d50b924..e7352119d 100644 --- a/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts +++ b/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts @@ -19,8 +19,8 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx [userAddress: string]: BigNumber; }; }; - constructor(token: AbstractBalanceAndProxyAllowanceFetcher) { - this._balanceAndProxyAllowanceFetcher = token; + constructor(balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher) { + this._balanceAndProxyAllowanceFetcher = balanceAndProxyAllowanceFetcher; this._balance = {}; this._proxyAllowance = {}; } diff --git a/packages/order-utils/test/exchange_transfer_simulator_test.ts b/packages/order-utils/test/exchange_transfer_simulator_test.ts index eeae42698..c8fc9fbf3 100644 --- a/packages/order-utils/test/exchange_transfer_simulator_test.ts +++ b/packages/order-utils/test/exchange_transfer_simulator_test.ts @@ -5,6 +5,7 @@ import * as chai from 'chai'; import 'make-promises-safe'; import { artifacts } from '../src/artifacts'; +import { assetProxyUtils } from '../src/asset_proxy_utils'; import { constants } from '../src/constants'; import { ExchangeTransferSimulator } from '../src/exchange_transfer_simulator'; import { DummyERC20TokenContract } from '../src/generated_contract_wrappers/dummy_e_r_c20_token'; @@ -28,7 +29,7 @@ describe('ExchangeTransferSimulator', async () => { let coinbase: string; let sender: string; let recipient: string; - let exampleTokenAddress: string; + let exampleAssetData: string; let exchangeTransferSimulator: ExchangeTransferSimulator; let txHash: string; let erc20ProxyAddress: string; @@ -66,7 +67,7 @@ describe('ExchangeTransferSimulator', async () => { totalSupply, ); - exampleTokenAddress = dummyERC20Token.address; + exampleAssetData = assetProxyUtils.encodeERC20AssetData(dummyERC20Token.address); }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -92,7 +93,7 @@ describe('ExchangeTransferSimulator', async () => { it("throws if the user doesn't have enough allowance", async () => { return expect( exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, + exampleAssetData, sender, recipient, transferAmount, @@ -108,7 +109,7 @@ describe('ExchangeTransferSimulator', async () => { await web3Wrapper.awaitTransactionSuccessAsync(txHash); return expect( exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, + exampleAssetData, sender, recipient, transferAmount, @@ -129,7 +130,7 @@ describe('ExchangeTransferSimulator', async () => { await web3Wrapper.awaitTransactionSuccessAsync(txHash); await exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, + exampleAssetData, sender, recipient, transferAmount, @@ -137,9 +138,9 @@ describe('ExchangeTransferSimulator', async () => { TransferType.Trade, ); const store = (exchangeTransferSimulator as any)._store; - const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender); - const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient); - const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender); + const senderBalance = await store.getBalanceAsync(exampleAssetData, sender); + const recipientBalance = await store.getBalanceAsync(exampleAssetData, recipient); + const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleAssetData, sender); expect(senderBalance).to.be.bignumber.equal(0); expect(recipientBalance).to.be.bignumber.equal(transferAmount); expect(senderProxyAllowance).to.be.bignumber.equal(0); @@ -158,7 +159,7 @@ describe('ExchangeTransferSimulator', async () => { ); await web3Wrapper.awaitTransactionSuccessAsync(txHash); await exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, + exampleAssetData, sender, recipient, transferAmount, @@ -166,9 +167,9 @@ describe('ExchangeTransferSimulator', async () => { TransferType.Trade, ); const store = (exchangeTransferSimulator as any)._store; - const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender); - const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient); - const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender); + const senderBalance = await store.getBalanceAsync(exampleAssetData, sender); + const recipientBalance = await store.getBalanceAsync(exampleAssetData, recipient); + const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleAssetData, sender); expect(senderBalance).to.be.bignumber.equal(0); expect(recipientBalance).to.be.bignumber.equal(transferAmount); expect(senderProxyAllowance).to.be.bignumber.equal(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); -- cgit From fe75660e88ed0c37c4f3d461a644bd9305bf6183 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 13 Jun 2018 16:01:01 +0200 Subject: Refactor ERC20 and ERC721 wrappers for V2 and introduce the assetWrapper superset --- .../src/abstract/abstract_asset_wrapper.ts | 10 +++++++ packages/contracts/src/utils/asset_wrapper.ts | 32 ++++++++++++++++++++ packages/contracts/src/utils/erc20_wrapper.ts | 27 ++++++++++++----- packages/contracts/src/utils/erc721_wrapper.ts | 35 +++++++++++++++++++++- 4 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 packages/contracts/src/abstract/abstract_asset_wrapper.ts create mode 100644 packages/contracts/src/utils/asset_wrapper.ts diff --git a/packages/contracts/src/abstract/abstract_asset_wrapper.ts b/packages/contracts/src/abstract/abstract_asset_wrapper.ts new file mode 100644 index 000000000..d7ab58fea --- /dev/null +++ b/packages/contracts/src/abstract/abstract_asset_wrapper.ts @@ -0,0 +1,10 @@ +import { BigNumber } from '@0xproject/utils'; + +export abstract class AbstractAssetWrapper { + public abstract getProxyId(): number; + public abstract async setBalancesAndAllowancesAsync(): Promise; + public abstract async getBalanceAsync(owner: string, assetData: string): Promise; + public abstract async getProxyAllowanceAsync(owner: string, assetData: string): Promise; + public abstract getTokenOwnerAddresses(): string[]; + public abstract getTokenAddresses(): string[]; +} diff --git a/packages/contracts/src/utils/asset_wrapper.ts b/packages/contracts/src/utils/asset_wrapper.ts new file mode 100644 index 000000000..4c345aa30 --- /dev/null +++ b/packages/contracts/src/utils/asset_wrapper.ts @@ -0,0 +1,32 @@ +import { assetProxyUtils } from '@0xproject/order-utils'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { AbstractAssetWrapper } from '../abstract/abstract_asset_wrapper'; + +interface ProxyIdToAssetWrappers { + [proxyId: number]: AbstractAssetWrapper; +} + +export class AssetWrapper { + private _proxyIdToAssetWrappers: ProxyIdToAssetWrappers; + constructor(assetWrappers: AbstractAssetWrapper[]) { + this._proxyIdToAssetWrappers = {}; + _.each(assetWrappers, assetWrapper => { + const proxyId = assetWrapper.getProxyId(); + this._proxyIdToAssetWrappers[proxyId] = assetWrapper; + }); + } + public async getBalanceAsync(owner: string, assetData: string): Promise { + const proxyId = assetProxyUtils.decodeAssetDataId(assetData); + const assetWrapper = this._proxyIdToAssetWrappers[proxyId]; + const balance = await assetWrapper.getBalanceAsync(owner, assetData); + return balance; + } + public async getProxyAllowanceAsync(owner: string, assetData: string): Promise { + const proxyId = assetProxyUtils.decodeAssetDataId(assetData); + const assetWrapper = this._proxyIdToAssetWrappers[proxyId]; + const balance = await assetWrapper.getProxyAllowanceAsync(owner, assetData); + return balance; + } +} diff --git a/packages/contracts/src/utils/erc20_wrapper.ts b/packages/contracts/src/utils/erc20_wrapper.ts index fc42fbff2..2d367ae1c 100644 --- a/packages/contracts/src/utils/erc20_wrapper.ts +++ b/packages/contracts/src/utils/erc20_wrapper.ts @@ -1,3 +1,4 @@ +import { assetProxyUtils } from '@0xproject/order-utils'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Provider } from 'ethereum-types'; @@ -18,6 +19,7 @@ export class ERC20Wrapper { private _provider: Provider; private _dummyTokenContracts: DummyERC20TokenContract[]; private _proxyContract?: ERC20ProxyContract; + private _proxyIdIfExists?: number; constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { this._dummyTokenContracts = []; this._web3Wrapper = new Web3Wrapper(provider); @@ -50,8 +52,13 @@ export class ERC20Wrapper { this._provider, txDefaults, ); + this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync(); return this._proxyContract; } + public getProxyId(): number { + this._validateProxyContractExistsOrThrow(); + return this._proxyIdIfExists as number; + } public async setBalancesAndAllowancesAsync(): Promise { this._validateDummyTokenContractsExistOrThrow(); this._validateProxyContractExistsOrThrow(); @@ -76,24 +83,28 @@ export class ERC20Wrapper { } } } - public async getBalanceAsync(owner: string, token: string): Promise { - const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === token); + public async getBalanceAsync(owner: string, assetData: string): Promise { + const erc20ProxyData = assetProxyUtils.decodeERC20AssetData(assetData); + const tokenAddress = erc20ProxyData.tokenAddress; + const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); if (_.isUndefined(tokenContractIfExists)) { - throw new Error(`Token: ${token} was not deployed through ERC20Wrapper`); + throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); } const balance = new BigNumber(await tokenContractIfExists.balanceOf.callAsync(owner)); return balance; } - public async getProxyAllowanceAsync(owner: string, token: string): Promise { + public async getProxyAllowanceAsync(owner: string, assetData: string): Promise { + const erc20ProxyData = assetProxyUtils.decodeERC20AssetData(assetData); + const tokenAddress = erc20ProxyData.tokenAddress; this._validateProxyContractExistsOrThrow(); - const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === token); + const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); if (_.isUndefined(tokenContractIfExists)) { - throw new Error(`Token: ${token} was not deployed through ERC20Wrapper`); + throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); } - const balance = new BigNumber( + const allowance = new BigNumber( await tokenContractIfExists.allowance.callAsync(owner, (this._proxyContract as ERC20ProxyContract).address), ); - return balance; + return allowance; } public async getBalancesAsync(): Promise { this._validateDummyTokenContractsExistOrThrow(); diff --git a/packages/contracts/src/utils/erc721_wrapper.ts b/packages/contracts/src/utils/erc721_wrapper.ts index b20d9acd2..6a6f10fa7 100644 --- a/packages/contracts/src/utils/erc721_wrapper.ts +++ b/packages/contracts/src/utils/erc721_wrapper.ts @@ -1,4 +1,4 @@ -import { generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Provider } from 'ethereum-types'; @@ -19,6 +19,7 @@ export class ERC721Wrapper { private _provider: Provider; private _dummyTokenContracts: DummyERC721TokenContract[]; private _proxyContract?: ERC721ProxyContract; + private _proxyIdIfExists?: number; private _initialTokenIdsByOwner: ERC721TokenIdsByOwner = {}; constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { this._web3Wrapper = new Web3Wrapper(provider); @@ -47,8 +48,13 @@ export class ERC721Wrapper { this._provider, txDefaults, ); + this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync(); return this._proxyContract; } + public getProxyId(): number { + this._validateProxyContractExistsOrThrow(); + return this._proxyIdIfExists as number; + } public async setBalancesAndAllowancesAsync(): Promise { this._validateDummyTokenContractsExistOrThrow(); this._validateProxyContractExistsOrThrow(); @@ -85,6 +91,33 @@ export class ERC721Wrapper { } } } + public async getBalanceAsync(owner: string, assetData: string): Promise { + const erc721ProxyData = assetProxyUtils.decodeERC721AssetData(assetData); + const tokenAddress = erc721ProxyData.tokenAddress; + const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); + if (_.isUndefined(tokenContractIfExists)) { + throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); + } + const tokenId = erc721ProxyData.tokenId; + const tokenOwner = await tokenContractIfExists.ownerOf.callAsync(tokenId); + const balance = tokenOwner === owner ? new BigNumber(1) : new BigNumber(0); + return balance; + } + public async getProxyAllowanceAsync(owner: string, assetData: string): Promise { + const erc721ProxyData = assetProxyUtils.decodeERC721AssetData(assetData); + const tokenAddress = erc721ProxyData.tokenAddress; + this._validateProxyContractExistsOrThrow(); + const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); + if (_.isUndefined(tokenContractIfExists)) { + throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); + } + const operator = (this._proxyContract as ERC721ProxyContract).address; + const didApproveAll = await tokenContractIfExists.isApprovedForAll.callAsync(owner, operator); + const tokenId = erc721ProxyData.tokenId; + const allowedAddress = await tokenContractIfExists.getApproved.callAsync(tokenId); + const allowance = allowedAddress === operator || didApproveAll ? new BigNumber(1) : new BigNumber(0); + return allowance; + } public async getBalancesAsync(): Promise { this._validateDummyTokenContractsExistOrThrow(); this._validateBalancesAndAllowancesSetOrThrow(); -- cgit From 61243b418e4d962cd8d8a1d7a49f04510b3c1c7f Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 13 Jun 2018 16:09:04 +0200 Subject: Implement initial set of orderFill combinatorial tests --- .../src/utils/core_combinatorial_utils.ts | 445 +++++++++++++++++++++ packages/contracts/src/utils/exchange_wrapper.ts | 4 + packages/contracts/src/utils/new_order_factory.ts | 130 ++---- packages/contracts/src/utils/order_info_utils.ts | 44 -- packages/contracts/src/utils/order_utils.ts | 7 + ...le_asset_balance_and_proxy_allowance_fetcher.ts | 19 + .../simple_erc20_balance_and_allowance_fetcher.ts | 20 - .../src/utils/simple_filled_cancelled_fetcher.ts | 32 -- .../utils/simple_order_filled_cancelled_fetcher.ts | 24 ++ packages/contracts/src/utils/types.ts | 15 +- packages/contracts/test/combinatorial_tests.ts | 262 +----------- .../order-utils/src/exchange_transfer_simulator.ts | 10 +- packages/order-utils/src/order_state_utils.ts | 262 +++++++++--- 13 files changed, 784 insertions(+), 490 deletions(-) create mode 100644 packages/contracts/src/utils/core_combinatorial_utils.ts delete mode 100644 packages/contracts/src/utils/order_info_utils.ts create mode 100644 packages/contracts/src/utils/simple_asset_balance_and_proxy_allowance_fetcher.ts delete mode 100644 packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts delete mode 100644 packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts create mode 100644 packages/contracts/src/utils/simple_order_filled_cancelled_fetcher.ts diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts new file mode 100644 index 000000000..6fbd8304e --- /dev/null +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -0,0 +1,445 @@ +import { + assetProxyUtils, + BalanceAndProxyAllowanceLazyStore, + ExchangeTransferSimulator, + orderHashUtils, + OrderStateUtils, + OrderValidationUtils, +} from '@0xproject/order-utils'; +import { AssetProxyId, Order, SignatureType, SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import * as chai from 'chai'; +import { BlockParamLiteral, LogWithDecodedArgs, Provider, TxData } from 'ethereum-types'; +// import ethUtil = require('ethereumjs-util'); +import * as _ from 'lodash'; +import 'make-promises-safe'; + +import { ExchangeContract, FillContractEventArgs } from '../generated_contract_wrappers/exchange'; +import { artifacts } from '../utils/artifacts'; +import { expectRevertOrAlwaysFailingTransactionAsync } from '../utils/assertions'; +import { AssetWrapper } from '../utils/asset_wrapper'; +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 { NewOrderFactory } from '../utils/new_order_factory'; +import { orderUtils } from '../utils/order_utils'; +import { signingUtils } from '../utils/signing_utils'; +import { SimpleAssetBalanceAndProxyAllowanceFetcher } from '../utils/simple_asset_balance_and_proxy_allowance_fetcher'; +import { SimpleOrderFilledCancelledFetcher } from '../utils/simple_order_filled_cancelled_fetcher'; +import { + AssetDataScenario, + ExpirationTimeSecondsScenario, + FeeRecipientAddressScenario, + OrderAmountScenario, + OrderScenario, +} from '../utils/types'; + +chaiSetup.configure(); +const expect = chai.expect; + +const ERC721_PROXY_ID = 2; + +/** + * Instantiates a new instance of CoreCombinatorialUtils. Since this method has some + * required async setup, a factory method is required. + * @param web3Wrapper Web3Wrapper instance + * @param txDefaults Default Ethereum tx options + * @return CoreCombinatorialUtils instance + */ +export async function coreCombinatorialUtilsFactoryAsync( + web3Wrapper: Web3Wrapper, + txDefaults: Partial, +): Promise { + const userAddresses = await web3Wrapper.getAvailableAddressesAsync(); + const [ownerAddress, makerAddress, takerAddress] = userAddresses; + const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)]; + + const provider = web3Wrapper.getProvider(); + const erc20Wrapper = new ERC20Wrapper(provider, userAddresses, ownerAddress); + const erc721Wrapper = new ERC721Wrapper(provider, userAddresses, ownerAddress); + + const erc20EighteenDecimalTokenCount = 3; + const eighteenDecimals = new BigNumber(18); + const [ + erc20EighteenDecimalTokenA, + erc20EighteenDecimalTokenB, + zrxToken, + ] = await erc20Wrapper.deployDummyTokensAsync(erc20EighteenDecimalTokenCount, eighteenDecimals); + const zrxAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address); + + const erc20FiveDecimalTokenCount = 2; + const fiveDecimals = new BigNumber(18); + const [erc20FiveDecimalTokenA, erc20FiveDecimalTokenB] = await erc20Wrapper.deployDummyTokensAsync( + erc20FiveDecimalTokenCount, + fiveDecimals, + ); + const erc20Proxy = await erc20Wrapper.deployProxyAsync(); + await erc20Wrapper.setBalancesAndAllowancesAsync(); + + const [erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); + const erc721Proxy = await erc721Wrapper.deployProxyAsync(); + await erc721Wrapper.setBalancesAndAllowancesAsync(); + const erc721Balances = await erc721Wrapper.getBalancesAsync(); + + const assetWrapper = new AssetWrapper([erc20Wrapper, erc721Wrapper]); + + const exchangeContract = await ExchangeContract.deployFrom0xArtifactAsync( + artifacts.Exchange, + provider, + txDefaults, + zrxAssetData, + ); + const exchangeWrapper = new ExchangeWrapper(exchangeContract, provider); + await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, ownerAddress); + await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC721, erc721Proxy.address, ownerAddress); + + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, { + from: ownerAddress, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, { + from: ownerAddress, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + const orderFactory = new NewOrderFactory( + userAddresses, + zrxToken.address, + [erc20EighteenDecimalTokenA.address, erc20EighteenDecimalTokenB.address], + [erc20FiveDecimalTokenA.address, erc20FiveDecimalTokenB.address], + erc721Token, + erc721Balances, + exchangeContract.address, + ); + + const coreCombinatorialUtils = new CoreCombinatorialUtils( + orderFactory, + ownerAddress, + makerAddress, + makerPrivateKey, + takerAddress, + zrxAssetData, + exchangeWrapper, + assetWrapper, + ); + return coreCombinatorialUtils; +} + +export class CoreCombinatorialUtils { + public orderFactory: NewOrderFactory; + public ownerAddress: string; + public makerAddress: string; + public makerPrivateKey: Buffer; + public takerAddress: string; + public zrxAssetData: string; + public exchangeWrapper: ExchangeWrapper; + public assetWrapper: AssetWrapper; + public static generateOrderCombinations(): OrderScenario[] { + const feeRecipientScenarios = [FeeRecipientAddressScenario.EthUserAddress]; + const makerAssetAmountScenario = [OrderAmountScenario.NonZero]; + const takerAssetAmountScenario = [OrderAmountScenario.NonZero]; + const makerFeeScenario = [OrderAmountScenario.NonZero]; + const takerFeeScenario = [OrderAmountScenario.NonZero]; + const expirationTimeSecondsScenario = [ExpirationTimeSecondsScenario.InFuture]; + const makerAssetDataScenario = [ + AssetDataScenario.ERC20FiveDecimals, + AssetDataScenario.ERC20NonZRXEighteenDecimals, + AssetDataScenario.ERC721, + AssetDataScenario.ZRXFeeToken, + ]; + const takerAssetDataScenario = [ + AssetDataScenario.ERC20FiveDecimals, + AssetDataScenario.ERC20NonZRXEighteenDecimals, + AssetDataScenario.ERC721, + AssetDataScenario.ZRXFeeToken, + ]; + const orderScenarioArrays = CoreCombinatorialUtils._allPossibleCases([ + feeRecipientScenarios, + makerAssetAmountScenario, + takerAssetAmountScenario, + makerFeeScenario, + takerFeeScenario, + expirationTimeSecondsScenario, + makerAssetDataScenario, + takerAssetDataScenario, + ]); + + const orderScenarios = _.map(orderScenarioArrays, orderScenarioArray => { + const orderScenario: OrderScenario = { + feeRecipientScenario: orderScenarioArray[0] as FeeRecipientAddressScenario, + makerAssetAmountScenario: orderScenarioArray[1] as OrderAmountScenario, + takerAssetAmountScenario: orderScenarioArray[2] as OrderAmountScenario, + makerFeeScenario: orderScenarioArray[3] as OrderAmountScenario, + takerFeeScenario: orderScenarioArray[4] as OrderAmountScenario, + expirationTimeSecondsScenario: orderScenarioArray[5] as ExpirationTimeSecondsScenario, + makerAssetDataScenario: orderScenarioArray[6] as AssetDataScenario, + takerAssetDataScenario: orderScenarioArray[7] as AssetDataScenario, + }; + return orderScenario; + }); + + return orderScenarios; + } + private static _allPossibleCases(arrays: string[][]): string[][] { + if (arrays.length === 1) { + const remainingVals = _.map(arrays[0], val => { + return [val]; + }); + return remainingVals; + } else { + const result = []; + const allCasesOfRest = CoreCombinatorialUtils._allPossibleCases(arrays.slice(1)); // recur with the rest of array + // tslint:disable:prefer-for-of + for (let i = 0; i < allCasesOfRest.length; i++) { + for (let j = 0; j < arrays[0].length; j++) { + result.push([arrays[0][j], ...allCasesOfRest[i]]); + } + } + // tslint:enable:prefer-for-of + return result; + } + } + constructor( + orderFactory: NewOrderFactory, + ownerAddress: string, + makerAddress: string, + makerPrivateKey: Buffer, + takerAddress: string, + zrxAssetData: string, + exchangeWrapper: ExchangeWrapper, + assetWrapper: AssetWrapper, + ) { + this.orderFactory = orderFactory; + this.ownerAddress = ownerAddress; + this.makerAddress = makerAddress; + this.makerPrivateKey = makerPrivateKey; + this.takerAddress = takerAddress; + this.zrxAssetData = zrxAssetData; + this.exchangeWrapper = exchangeWrapper; + this.assetWrapper = assetWrapper; + } + public async testFillOrderScenarioAsync(order: Order, provider: Provider): Promise { + // 2. Sign order + const orderHashBuff = orderHashUtils.getOrderHashBuff(order); + const signature = signingUtils.signMessage(orderHashBuff, this.makerPrivateKey, SignatureType.EthSign); + const signedOrder = { + ...order, + signature: `0x${signature.toString('hex')}`, + }; + + // 3. Permutate the maker and taker balance/allowance scenarios + + // 4. Figure out fill amount OR error + const balanceAndProxyAllowanceFetcher = new SimpleAssetBalanceAndProxyAllowanceFetcher(this.assetWrapper); + const orderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher( + this.exchangeWrapper, + this.zrxAssetData, + ); + const orderStateUtils = new OrderStateUtils(balanceAndProxyAllowanceFetcher, orderFilledCancelledFetcher); + + const fillableTakerAssetAmount = await orderStateUtils.getMaxFillableTakerAssetAmountAsync( + signedOrder, + this.takerAddress, + ); + + // If order is fillable, decide how much to fill + // TODO: Make this configurable + const takerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.takerAssetData); + const makerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.makerAssetData); + const isEitherAssetERC721 = takerAssetProxyId === ERC721_PROXY_ID || makerAssetProxyId === ERC721_PROXY_ID; + const takerAssetFillAmount = isEitherAssetERC721 + ? fillableTakerAssetAmount + : fillableTakerAssetAmount.div(2).floor(); + + // 5. If I fill it by X, what are the resulting balances/allowances/filled amounts exp? + const orderValidationUtils = new OrderValidationUtils(orderFilledCancelledFetcher); + const lazyStore = new BalanceAndProxyAllowanceLazyStore(balanceAndProxyAllowanceFetcher); + const exchangeTransferSimulator = new ExchangeTransferSimulator(lazyStore); + + let isFillFailureExpected = false; + try { + await orderValidationUtils.validateFillOrderThrowIfInvalidAsync( + exchangeTransferSimulator, + provider, + signedOrder, + takerAssetFillAmount, + this.takerAddress, + this.zrxAssetData, + ); + } catch (err) { + isFillFailureExpected = true; + } + + await this._fillOrderAndAssertOutcomeAsync( + signedOrder, + takerAssetFillAmount, + lazyStore, + isFillFailureExpected, + provider, + ); + } + private async _fillOrderAndAssertOutcomeAsync( + signedOrder: SignedOrder, + takerAssetFillAmount: BigNumber, + lazyStore: BalanceAndProxyAllowanceLazyStore, + isFillFailureExpected: boolean, + provider: Provider, + ): Promise { + if (isFillFailureExpected) { + return expectRevertOrAlwaysFailingTransactionAsync( + this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { takerAssetFillAmount }), + ); + } + + const makerAddress = signedOrder.makerAddress; + const makerAssetData = signedOrder.makerAssetData; + const takerAssetData = signedOrder.takerAssetData; + const feeRecipient = signedOrder.feeRecipientAddress; + + const expMakerAssetBalanceOfMaker = await lazyStore.getBalanceAsync(makerAssetData, makerAddress); + const expMakerAssetAllowanceOfMaker = await lazyStore.getProxyAllowanceAsync(makerAssetData, makerAddress); + const expTakerAssetBalanceOfMaker = await lazyStore.getBalanceAsync(takerAssetData, makerAddress); + const expZRXAssetBalanceOfMaker = await lazyStore.getBalanceAsync(this.zrxAssetData, makerAddress); + const expZRXAssetAllowanceOfMaker = await lazyStore.getProxyAllowanceAsync(this.zrxAssetData, makerAddress); + const expTakerAssetBalanceOfTaker = await lazyStore.getBalanceAsync(takerAssetData, this.takerAddress); + const expTakerAssetAllowanceOfTaker = await lazyStore.getProxyAllowanceAsync(takerAssetData, this.takerAddress); + const expMakerAssetBalanceOfTaker = await lazyStore.getBalanceAsync(makerAssetData, this.takerAddress); + const expZRXAssetBalanceOfTaker = await lazyStore.getBalanceAsync(this.zrxAssetData, this.takerAddress); + const expZRXAssetAllowanceOfTaker = await lazyStore.getProxyAllowanceAsync( + this.zrxAssetData, + this.takerAddress, + ); + const expZRXAssetBalanceOfFeeRecipient = await lazyStore.getBalanceAsync(this.zrxAssetData, feeRecipient); + + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + const initialFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash); + const expFilledTakerAmount = initialFilledTakerAmount.add(takerAssetFillAmount); + + const expFilledMakerAmount = orderUtils.getPartialAmount( + takerAssetFillAmount, + signedOrder.takerAssetAmount, + signedOrder.makerAssetAmount, + ); + + // - Let's fill the order! + const txReceipt = await this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { + takerAssetFillAmount, + }); + + const actFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash); + expect(actFilledTakerAmount).to.be.bignumber.equal(expFilledTakerAmount, 'filledTakerAmount'); + + expect(txReceipt.logs.length).to.be.equal(1, 'logs length'); + // tslint:disable-next-line:no-unnecessary-type-assertion + const log = txReceipt.logs[0] as LogWithDecodedArgs; + expect(log.args.makerAddress).to.be.equal(makerAddress, 'log.args.makerAddress'); + expect(log.args.takerAddress).to.be.equal(this.takerAddress, 'log.args.this.takerAddress'); + expect(log.args.feeRecipientAddress).to.be.equal(feeRecipient, 'log.args.feeRecipientAddress'); + expect(log.args.makerAssetFilledAmount).to.be.bignumber.equal( + expFilledMakerAmount, + 'log.args.makerAssetFilledAmount', + ); + expect(log.args.takerAssetFilledAmount).to.be.bignumber.equal( + takerAssetFillAmount, + 'log.args.takerAssetFilledAmount', + ); + const expMakerFeePaid = orderUtils.getPartialAmount( + expFilledTakerAmount, + signedOrder.takerAssetAmount, + signedOrder.makerFee, + ); + expect(log.args.makerFeePaid).to.be.bignumber.equal(expMakerFeePaid, 'log.args.makerFeePaid'); + const expTakerFeePaid = orderUtils.getPartialAmount( + expFilledTakerAmount, + signedOrder.takerAssetAmount, + signedOrder.takerFee, + ); + expect(log.args.takerFeePaid).to.be.bignumber.equal(expTakerFeePaid, 'logs.args.takerFeePaid'); + expect(log.args.orderHash).to.be.equal(orderHash, 'log.args.orderHash'); + expect(log.args.makerAssetData).to.be.equal(makerAssetData, 'log.args.makerAssetData'); + expect(log.args.takerAssetData).to.be.equal(takerAssetData, 'log.args.takerAssetData'); + + const actMakerAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, makerAssetData); + expect(actMakerAssetBalanceOfMaker).to.be.bignumber.equal( + expMakerAssetBalanceOfMaker, + 'makerAssetBalanceOfMaker', + ); + + const actMakerAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync( + makerAddress, + makerAssetData, + ); + expect(actMakerAssetAllowanceOfMaker).to.be.bignumber.equal( + expMakerAssetAllowanceOfMaker, + 'makerAssetAllowanceOfMaker', + ); + + const actTakerAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, takerAssetData); + expect(actTakerAssetBalanceOfMaker).to.be.bignumber.equal( + expTakerAssetBalanceOfMaker, + 'takerAssetBalanceOfMaker', + ); + + const actZRXAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, this.zrxAssetData); + expect(actZRXAssetBalanceOfMaker).to.be.bignumber.equal(expZRXAssetBalanceOfMaker, 'ZRXAssetBalanceOfMaker'); + + const actZRXAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync( + makerAddress, + this.zrxAssetData, + ); + expect(actZRXAssetAllowanceOfMaker).to.be.bignumber.equal( + expZRXAssetAllowanceOfMaker, + 'ZRXAssetAllowanceOfMaker', + ); + + const actTakerAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, takerAssetData); + expect(actTakerAssetBalanceOfTaker).to.be.bignumber.equal( + expTakerAssetBalanceOfTaker, + 'TakerAssetBalanceOfTaker', + ); + + const actTakerAssetAllowanceOfTaker = await this.assetWrapper.getProxyAllowanceAsync( + this.takerAddress, + takerAssetData, + ); + + expect(actTakerAssetAllowanceOfTaker).to.be.bignumber.equal( + expTakerAssetAllowanceOfTaker, + 'TakerAssetAllowanceOfTaker', + ); + + const actMakerAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, makerAssetData); + expect(actMakerAssetBalanceOfTaker).to.be.bignumber.equal( + expMakerAssetBalanceOfTaker, + 'MakerAssetBalanceOfTaker', + ); + + const actZRXAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, this.zrxAssetData); + expect(actZRXAssetBalanceOfTaker).to.be.bignumber.equal(expZRXAssetBalanceOfTaker, 'ZRXAssetBalanceOfTaker'); + + const actZRXAssetAllowanceOfTaker = await this.assetWrapper.getProxyAllowanceAsync( + this.takerAddress, + this.zrxAssetData, + ); + expect(actZRXAssetAllowanceOfTaker).to.be.bignumber.equal( + expZRXAssetAllowanceOfTaker, + 'ZRXAssetAllowanceOfTaker', + ); + + const actZRXAssetBalanceOfFeeRecipient = await this.assetWrapper.getBalanceAsync( + feeRecipient, + this.zrxAssetData, + ); + expect(actZRXAssetBalanceOfFeeRecipient).to.be.bignumber.equal( + expZRXAssetBalanceOfFeeRecipient, + 'ZRXAssetBalanceOfFeeRecipient', + ); + } +} diff --git a/packages/contracts/src/utils/exchange_wrapper.ts b/packages/contracts/src/utils/exchange_wrapper.ts index 1ecabcf64..e74b91fe2 100644 --- a/packages/contracts/src/utils/exchange_wrapper.ts +++ b/packages/contracts/src/utils/exchange_wrapper.ts @@ -227,6 +227,10 @@ export class ExchangeWrapper { const filledAmount = new BigNumber(await this._exchange.filled.callAsync(orderHashHex)); return filledAmount; } + public async isCancelledAsync(orderHashHex: string): Promise { + const isCancelled = await this._exchange.cancelled.callAsync(orderHashHex); + return isCancelled; + } public async getOrderInfoAsync(signedOrder: SignedOrder): Promise { const orderInfo = (await this._exchange.getOrderInfo.callAsync(signedOrder)) as OrderInfo; return orderInfo; diff --git a/packages/contracts/src/utils/new_order_factory.ts b/packages/contracts/src/utils/new_order_factory.ts index a4ded4230..715e38d6d 100644 --- a/packages/contracts/src/utils/new_order_factory.ts +++ b/packages/contracts/src/utils/new_order_factory.ts @@ -2,7 +2,7 @@ import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-util import { Order } from '@0xproject/types'; import { BigNumber, errorUtils } from '@0xproject/utils'; -import { DummyERC721TokenContract } from '../contract_wrappers/generated/dummy_e_r_c721_token'; +import { DummyERC721TokenContract } from '../generated_contract_wrappers/dummy_e_r_c721_token'; import { constants } from './constants'; import { @@ -11,20 +11,13 @@ import { ExpirationTimeSecondsScenario, FeeRecipientAddressScenario, OrderAmountScenario, + OrderScenario, } from './types'; const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10000000000000000000); const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100000000000000000); const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1000000); const ONE_NFT_UNIT = new BigNumber(1); -const TEN_MINUTES_MS = 1000 * 60 * 10; - -/* - * TODO: - * - Write function that given an order, fillAmount, retrieves orderRelevantState and maps it to expected test outcome. - * - Write function that generates order permutations. - * - Write functions for other steps that must be permutated - */ export class NewOrderFactory { private _userAddresses: string[]; @@ -51,16 +44,7 @@ export class NewOrderFactory { this._erc721Balances = erc721Balances; this._exchangeAddress = exchangeAddress; } - public generateOrder( - feeRecipientScenario: FeeRecipientAddressScenario, - makerAssetAmountScenario: OrderAmountScenario, - takerAssetAmountScenario: OrderAmountScenario, - makerFeeScenario: OrderAmountScenario, - takerFeeScenario: OrderAmountScenario, - expirationTimeSecondsScenario: ExpirationTimeSecondsScenario, - makerAssetDataScenario: AssetDataScenario, - takerAssetDataScenario: AssetDataScenario, - ): Order { + public generateOrder(orderScenario: OrderScenario): Order { const makerAddress = this._userAddresses[1]; const takerAddress = this._userAddresses[2]; const erc721MakerAssetIds = this._erc721Balances[makerAddress][this._erc721Token.address]; @@ -74,7 +58,7 @@ export class NewOrderFactory { let makerAssetData; let takerAssetData; - switch (feeRecipientScenario) { + switch (orderScenario.feeRecipientScenario) { case FeeRecipientAddressScenario.BurnAddress: feeRecipientAddress = constants.NULL_ADDRESS; break; @@ -82,135 +66,102 @@ export class NewOrderFactory { feeRecipientAddress = this._userAddresses[4]; break; default: - throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', feeRecipientScenario); + throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', orderScenario.feeRecipientScenario); } - const invalidAssetProxyIdHex = '0A'; - switch (makerAssetDataScenario) { + switch (orderScenario.makerAssetDataScenario) { case AssetDataScenario.ZRXFeeToken: - makerAssetData = assetProxyUtils.encodeERC20ProxyData(this._zrxAddress); + makerAssetData = assetProxyUtils.encodeERC20AssetData(this._zrxAddress); break; case AssetDataScenario.ERC20NonZRXEighteenDecimals: - makerAssetData = assetProxyUtils.encodeERC20ProxyData( + makerAssetData = assetProxyUtils.encodeERC20AssetData( this._nonZrxERC20EighteenDecimalTokenAddresses[0], ); break; case AssetDataScenario.ERC20FiveDecimals: - makerAssetData = assetProxyUtils.encodeERC20ProxyData(this._erc20FiveDecimalTokenAddresses[0]); - break; - case AssetDataScenario.ERC20InvalidAssetProxyId: { - const validAssetData = assetProxyUtils.encodeERC20ProxyData( - this._nonZrxERC20EighteenDecimalTokenAddresses[0], - ); - makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; - break; - } - case AssetDataScenario.ERC721ValidAssetProxyId: - makerAssetData = assetProxyUtils.encodeERC721ProxyData( - this._erc721Token.address, - erc721MakerAssetIds[0], - ); + makerAssetData = assetProxyUtils.encodeERC20AssetData(this._erc20FiveDecimalTokenAddresses[0]); break; - case AssetDataScenario.ERC721InvalidAssetProxyId: { - const validAssetData = assetProxyUtils.encodeERC721ProxyData( + case AssetDataScenario.ERC721: + makerAssetData = assetProxyUtils.encodeERC721AssetData( this._erc721Token.address, erc721MakerAssetIds[0], ); - makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; break; - } default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', makerAssetDataScenario); + throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); } - switch (takerAssetDataScenario) { + switch (orderScenario.takerAssetDataScenario) { case AssetDataScenario.ZRXFeeToken: - takerAssetData = assetProxyUtils.encodeERC20ProxyData(this._zrxAddress); + takerAssetData = assetProxyUtils.encodeERC20AssetData(this._zrxAddress); break; case AssetDataScenario.ERC20NonZRXEighteenDecimals: - takerAssetData = assetProxyUtils.encodeERC20ProxyData( + takerAssetData = assetProxyUtils.encodeERC20AssetData( this._nonZrxERC20EighteenDecimalTokenAddresses[1], ); break; case AssetDataScenario.ERC20FiveDecimals: - takerAssetData = assetProxyUtils.encodeERC20ProxyData(this._erc20FiveDecimalTokenAddresses[1]); + takerAssetData = assetProxyUtils.encodeERC20AssetData(this._erc20FiveDecimalTokenAddresses[1]); break; - case AssetDataScenario.ERC20InvalidAssetProxyId: { - const validAssetData = assetProxyUtils.encodeERC20ProxyData( - this._nonZrxERC20EighteenDecimalTokenAddresses[1], - ); - takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; - break; - } - case AssetDataScenario.ERC721ValidAssetProxyId: - takerAssetData = assetProxyUtils.encodeERC721ProxyData( - this._erc721Token.address, - erc721TakerAssetIds[0], - ); - break; - case AssetDataScenario.ERC721InvalidAssetProxyId: { - const validAssetData = assetProxyUtils.encodeERC721ProxyData( + case AssetDataScenario.ERC721: + takerAssetData = assetProxyUtils.encodeERC721AssetData( this._erc721Token.address, erc721TakerAssetIds[0], ); - takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; break; - } default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', takerAssetDataScenario); + throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); } - switch (makerAssetAmountScenario) { + switch (orderScenario.makerAssetAmountScenario) { case OrderAmountScenario.NonZero: - switch (makerAssetDataScenario) { + switch (orderScenario.makerAssetDataScenario) { + case AssetDataScenario.ZRXFeeToken: case AssetDataScenario.ERC20NonZRXEighteenDecimals: - case AssetDataScenario.ERC20InvalidAssetProxyId: makerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; break; case AssetDataScenario.ERC20FiveDecimals: makerAssetAmount = TEN_UNITS_FIVE_DECIMALS; break; - case AssetDataScenario.ERC721ValidAssetProxyId: - case AssetDataScenario.ERC721InvalidAssetProxyId: + case AssetDataScenario.ERC721: makerAssetAmount = ONE_NFT_UNIT; break; default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', makerAssetDataScenario); + throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); } break; case OrderAmountScenario.Zero: makerAssetAmount = new BigNumber(0); break; default: - throw errorUtils.spawnSwitchErr('OrderAmountScenario', makerAssetAmountScenario); + throw errorUtils.spawnSwitchErr('OrderAmountScenario', orderScenario.makerAssetAmountScenario); } - switch (takerAssetAmountScenario) { + switch (orderScenario.takerAssetAmountScenario) { case OrderAmountScenario.NonZero: - switch (takerAssetDataScenario) { + switch (orderScenario.takerAssetDataScenario) { case AssetDataScenario.ERC20NonZRXEighteenDecimals: - case AssetDataScenario.ERC20InvalidAssetProxyId: + case AssetDataScenario.ZRXFeeToken: takerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; break; case AssetDataScenario.ERC20FiveDecimals: takerAssetAmount = TEN_UNITS_FIVE_DECIMALS; break; - case AssetDataScenario.ERC721ValidAssetProxyId: - case AssetDataScenario.ERC721InvalidAssetProxyId: + case AssetDataScenario.ERC721: takerAssetAmount = ONE_NFT_UNIT; break; default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', takerAssetDataScenario); + throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); } break; case OrderAmountScenario.Zero: takerAssetAmount = new BigNumber(0); break; default: - throw errorUtils.spawnSwitchErr('OrderAmountScenario', takerAssetAmountScenario); + throw errorUtils.spawnSwitchErr('OrderAmountScenario', orderScenario.takerAssetAmountScenario); } - switch (makerFeeScenario) { + switch (orderScenario.makerFeeScenario) { case OrderAmountScenario.NonZero: makerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; break; @@ -218,10 +169,10 @@ export class NewOrderFactory { makerFee = new BigNumber(0); break; default: - throw errorUtils.spawnSwitchErr('OrderAmountScenario', makerFeeScenario); + throw errorUtils.spawnSwitchErr('OrderAmountScenario', orderScenario.makerFeeScenario); } - switch (takerFeeScenario) { + switch (orderScenario.takerFeeScenario) { case OrderAmountScenario.NonZero: takerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; break; @@ -229,18 +180,21 @@ export class NewOrderFactory { takerFee = new BigNumber(0); break; default: - throw errorUtils.spawnSwitchErr('OrderAmountScenario', takerFeeScenario); + throw errorUtils.spawnSwitchErr('OrderAmountScenario', orderScenario.takerFeeScenario); } - switch (expirationTimeSecondsScenario) { + switch (orderScenario.expirationTimeSecondsScenario) { case ExpirationTimeSecondsScenario.InFuture: - expirationTimeSeconds = new BigNumber(Date.now() + TEN_MINUTES_MS); + expirationTimeSeconds = new BigNumber(2524604400); // Close to infinite break; case ExpirationTimeSecondsScenario.InPast: - expirationTimeSeconds = new BigNumber(Date.now() - TEN_MINUTES_MS); + expirationTimeSeconds = new BigNumber(0); // Jan 1, 1970 break; default: - throw errorUtils.spawnSwitchErr('ExpirationTimeSecondsScenario', expirationTimeSecondsScenario); + throw errorUtils.spawnSwitchErr( + 'ExpirationTimeSecondsScenario', + orderScenario.expirationTimeSecondsScenario, + ); } const order: Order = { diff --git a/packages/contracts/src/utils/order_info_utils.ts b/packages/contracts/src/utils/order_info_utils.ts deleted file mode 100644 index 9df627da3..000000000 --- a/packages/contracts/src/utils/order_info_utils.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { assetProxyUtils, OrderStateUtils } from '@0xproject/order-utils'; -import { SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; - -import { ExchangeContract } from '../contract_wrappers/generated/exchange'; - -import { constants } from './constants'; -import { ERC20Wrapper } from './erc20_wrapper'; -import { SimpleERC20BalanceAndProxyAllowanceFetcher } from './simple_erc20_balance_and_allowance_fetcher'; -import { SimpleOrderFilledCancelledFetcher } from './simple_filled_cancelled_fetcher'; - -export class OrderInfoUtils { - private _orderStateUtils: OrderStateUtils; - private _erc20Wrapper: ERC20Wrapper; - constructor(exchangeContract: ExchangeContract, erc20Wrapper: ERC20Wrapper, zrxAddress: string) { - this._erc20Wrapper = erc20Wrapper; - const simpleOrderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher(exchangeContract, zrxAddress); - const simpleERC20BalanceAndProxyAllowanceFetcher = new SimpleERC20BalanceAndProxyAllowanceFetcher(erc20Wrapper); - this._orderStateUtils = new OrderStateUtils( - simpleERC20BalanceAndProxyAllowanceFetcher, - simpleOrderFilledCancelledFetcher, - ); - } - public async getFillableTakerAssetAmountAsync(signedOrder: SignedOrder, takerAddress: string): Promise { - const orderRelevantState = await this._orderStateUtils.getOrderRelevantStateAsync(signedOrder); - console.log('orderRelevantState', orderRelevantState); - if (takerAddress === constants.NULL_ADDRESS) { - return orderRelevantState.remainingFillableTakerAssetAmount; - } - const takerAssetData = assetProxyUtils.decodeERC20ProxyData(signedOrder.takerAssetData); - const takerBalance = await this._erc20Wrapper.getBalanceAsync(takerAddress, takerAssetData.tokenAddress); - const takerAllowance = await this._erc20Wrapper.getProxyAllowanceAsync( - takerAddress, - takerAssetData.tokenAddress, - ); - // TODO: We also need to make sure taker has sufficient ZRX for fees... - const fillableTakerAssetAmount = BigNumber.min([ - takerBalance, - takerAllowance, - orderRelevantState.remainingFillableTakerAssetAmount, - ]); - return fillableTakerAssetAmount; - } -} diff --git a/packages/contracts/src/utils/order_utils.ts b/packages/contracts/src/utils/order_utils.ts index 2a8791e4c..40643fa75 100644 --- a/packages/contracts/src/utils/order_utils.ts +++ b/packages/contracts/src/utils/order_utils.ts @@ -4,6 +4,13 @@ import { BigNumber } from '@0xproject/utils'; import { CancelOrder, MatchOrder } from './types'; export const orderUtils = { + getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber { + const partialAmount = numerator + .mul(target) + .div(denominator) + .floor(); + return partialAmount; + }, createFill: (signedOrder: SignedOrder, takerAssetFillAmount?: BigNumber) => { const fill = { order: orderUtils.getOrderWithoutExchangeAddress(signedOrder), diff --git a/packages/contracts/src/utils/simple_asset_balance_and_proxy_allowance_fetcher.ts b/packages/contracts/src/utils/simple_asset_balance_and_proxy_allowance_fetcher.ts new file mode 100644 index 000000000..a295a40c4 --- /dev/null +++ b/packages/contracts/src/utils/simple_asset_balance_and_proxy_allowance_fetcher.ts @@ -0,0 +1,19 @@ +import { AbstractBalanceAndProxyAllowanceFetcher } from '@0xproject/order-utils'; +import { BigNumber } from '@0xproject/utils'; + +import { AssetWrapper } from './asset_wrapper'; + +export class SimpleAssetBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher { + private _assetWrapper: AssetWrapper; + constructor(assetWrapper: AssetWrapper) { + this._assetWrapper = assetWrapper; + } + public async getBalanceAsync(assetData: string, userAddress: string): Promise { + const balance = await this._assetWrapper.getBalanceAsync(userAddress, assetData); + return balance; + } + public async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise { + const proxyAllowance = await this._assetWrapper.getProxyAllowanceAsync(userAddress, assetData); + return proxyAllowance; + } +} diff --git a/packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts b/packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts deleted file mode 100644 index 6eed9227a..000000000 --- a/packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { AbstractBalanceAndProxyAllowanceFetcher } from '@0xproject/order-utils'; -import { BigNumber } from '@0xproject/utils'; - -import { ERC20Wrapper } from './erc20_wrapper'; - -// TODO(fabio): Refactor this to also work for ERC721! -export class SimpleERC20BalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher { - private _erc20TokenWrapper: ERC20Wrapper; - constructor(erc20TokenWrapper: ERC20Wrapper) { - this._erc20TokenWrapper = erc20TokenWrapper; - } - public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise { - const balance = await this._erc20TokenWrapper.getBalanceAsync(userAddress, tokenAddress); - return balance; - } - public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise { - const proxyAllowance = await this._erc20TokenWrapper.getProxyAllowanceAsync(userAddress, tokenAddress); - return proxyAllowance; - } -} diff --git a/packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts b/packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts deleted file mode 100644 index 0a80637cc..000000000 --- a/packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils'; -import { BigNumber } from '@0xproject/utils'; -import { BlockParamLiteral } from 'ethereum-types'; - -import { - CancelContractEventArgs, - ExchangeContract, - FillContractEventArgs, -} from '../contract_wrappers/generated/exchange'; - -export class SimpleOrderFilledCancelledFetcher implements AbstractOrderFilledCancelledFetcher { - private _exchangeContract: ExchangeContract; - private _zrxAddress: string; - constructor(exchange: ExchangeContract, zrxAddress: string) { - this._exchangeContract = exchange; - this._zrxAddress = zrxAddress; - } - public async getFilledTakerAmountAsync(orderHash: string): Promise { - const filledTakerAmount = new BigNumber(await this._exchangeContract.filled.callAsync(orderHash)); - return filledTakerAmount; - } - public async isOrderCancelledAsync(orderHash: string): Promise { - const methodOpts = { - defaultBlock: BlockParamLiteral.Latest, - }; - const isCancelled = await this._exchangeContract.cancelled.callAsync(orderHash); - return isCancelled; - } - public getZRXTokenAddress(): string { - return this._zrxAddress; - } -} diff --git a/packages/contracts/src/utils/simple_order_filled_cancelled_fetcher.ts b/packages/contracts/src/utils/simple_order_filled_cancelled_fetcher.ts new file mode 100644 index 000000000..24afe36b7 --- /dev/null +++ b/packages/contracts/src/utils/simple_order_filled_cancelled_fetcher.ts @@ -0,0 +1,24 @@ +import { AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils'; +import { BigNumber } from '@0xproject/utils'; + +import { ExchangeWrapper } from './exchange_wrapper'; + +export class SimpleOrderFilledCancelledFetcher implements AbstractOrderFilledCancelledFetcher { + private _exchangeWrapper: ExchangeWrapper; + private _zrxAssetData: string; + constructor(exchange: ExchangeWrapper, zrxAssetData: string) { + this._exchangeWrapper = exchange; + this._zrxAssetData = zrxAssetData; + } + public async getFilledTakerAmountAsync(orderHash: string): Promise { + const filledTakerAmount = new BigNumber(await this._exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash)); + return filledTakerAmount; + } + public async isOrderCancelledAsync(orderHash: string): Promise { + const isCancelled = await this._exchangeWrapper.isCancelledAsync(orderHash); + return isCancelled; + } + public getZRXAssetData(): string { + return this._zrxAssetData; + } +} diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index a6e1cd6d0..77e8d867e 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -169,10 +169,19 @@ export enum ExpirationTimeSecondsScenario { } export enum AssetDataScenario { - ERC721ValidAssetProxyId = 'ERC721_VALID_ASSET_PROXY_ID', - ERC721InvalidAssetProxyId = 'ERC721_INVALID_ASSET_PROXY_ID', + ERC721 = 'ERC721', ZRXFeeToken = 'ZRX_FEE_TOKEN', - ERC20InvalidAssetProxyId = 'ERC20_INVALID_ASSET_PROXY_ID', ERC20FiveDecimals = 'ERC20_FIVE_DECIMALS', ERC20NonZRXEighteenDecimals = 'ERC20_NON_ZRX_EIGHTEEN_DECIMALS', } + +export interface OrderScenario { + feeRecipientScenario: FeeRecipientAddressScenario; + makerAssetAmountScenario: OrderAmountScenario; + takerAssetAmountScenario: OrderAmountScenario; + makerFeeScenario: OrderAmountScenario; + takerFeeScenario: OrderAmountScenario; + expirationTimeSecondsScenario: ExpirationTimeSecondsScenario; + makerAssetDataScenario: AssetDataScenario; + takerAssetDataScenario: AssetDataScenario; +} diff --git a/packages/contracts/test/combinatorial_tests.ts b/packages/contracts/test/combinatorial_tests.ts index 7a118f6ac..c7de8d7e3 100644 --- a/packages/contracts/test/combinatorial_tests.ts +++ b/packages/contracts/test/combinatorial_tests.ts @@ -1,266 +1,48 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { assetProxyUtils, crypto, orderHashUtils, OrderStateUtils } from '@0xproject/order-utils'; -import { AssetProxyId, SignatureType, SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; -import * as chai from 'chai'; -import { LogWithDecodedArgs } from 'ethereum-types'; -import ethUtil = require('ethereumjs-util'); -import 'make-promises-safe'; +import * as _ from 'lodash'; -import { DummyERC20TokenContract } from '../src/contract_wrappers/generated/dummy_e_r_c20_token'; -import { DummyERC721TokenContract } from '../src/contract_wrappers/generated/dummy_e_r_c721_token'; -import { ERC20ProxyContract } from '../src/contract_wrappers/generated/e_r_c20_proxy'; -import { ERC721ProxyContract } from '../src/contract_wrappers/generated/e_r_c721_proxy'; -import { - CancelContractEventArgs, - ExchangeContract, - FillContractEventArgs, -} from '../src/contract_wrappers/generated/exchange'; -import { artifacts } from '../src/utils/artifacts'; import { chaiSetup } from '../src/utils/chai_setup'; -import { constants } from '../src/utils/constants'; -import { ERC20Wrapper } from '../src/utils/erc20_wrapper'; -import { ERC721Wrapper } from '../src/utils/erc721_wrapper'; -import { ExchangeWrapper } from '../src/utils/exchange_wrapper'; -import { NewOrderFactory } from '../src/utils/new_order_factory'; -import { OrderInfoUtils } from '../src/utils/order_info_utils'; -import { orderUtils } from '../src/utils/order_utils'; -import { signingUtils } from '../src/utils/signing_utils'; -import { - AssetDataScenario, - ContractName, - ERC20BalancesByOwner, - ExpirationTimeSecondsScenario, - FeeRecipientAddressScenario, - OrderAmountScenario, - OrderStatus, -} from '../src/utils/types'; - +import { CoreCombinatorialUtils, coreCombinatorialUtilsFactoryAsync } from '../src/utils/core_combinatorial_utils'; import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper'; +import { OrderScenario } from '../src/utils/types'; + chaiSetup.configure(); -const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); describe('Combinatorial tests', () => { - let newOrderFactory: NewOrderFactory; - let usedAddresses: string[]; - - let makerAddress: string; - let owner: string; - let takerAddress: string; - let feeRecipientAddress: string; - - let erc20EighteenDecimalTokenA: DummyERC20TokenContract; - let erc20EighteenDecimalTokenB: DummyERC20TokenContract; - let erc20FiveDecimalTokenA: DummyERC20TokenContract; - let erc20FiveDecimalTokenB: DummyERC20TokenContract; - let zrxToken: DummyERC20TokenContract; - let erc721Token: DummyERC721TokenContract; - let exchange: ExchangeContract; - let erc20Proxy: ERC20ProxyContract; - let erc721Proxy: ERC721ProxyContract; - - let erc20Balances: ERC20BalancesByOwner; - let exchangeWrapper: ExchangeWrapper; - let erc20Wrapper: ERC20Wrapper; - let erc721Wrapper: ERC721Wrapper; - - let erc721MakerAssetIds: BigNumber[]; - let erc721TakerAssetIds: BigNumber[]; - - let defaultMakerAssetAddress: string; - let defaultTakerAssetAddress: string; + let coreCombinatorialUtils: CoreCombinatorialUtils; before(async () => { await blockchainLifecycle.startAsync(); + coreCombinatorialUtils = await coreCombinatorialUtilsFactoryAsync(web3Wrapper, txDefaults); }); after(async () => { await blockchainLifecycle.revertAsync(); }); - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - usedAddresses = [owner, makerAddress, takerAddress, feeRecipientAddress] = accounts; - - erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); - erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); - - const erc20EighteenDecimalTokenCount = 3; - const eighteenDecimals = new BigNumber(18); - [erc20EighteenDecimalTokenA, erc20EighteenDecimalTokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( - erc20EighteenDecimalTokenCount, - eighteenDecimals, - ); - - const erc20FiveDecimalTokenCount = 2; - const fiveDecimals = new BigNumber(18); - [erc20FiveDecimalTokenA, erc20FiveDecimalTokenB] = await erc20Wrapper.deployDummyTokensAsync( - erc20FiveDecimalTokenCount, - fiveDecimals, - ); - erc20Proxy = await erc20Wrapper.deployProxyAsync(); - await erc20Wrapper.setBalancesAndAllowancesAsync(); - - [erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); - erc721Proxy = await erc721Wrapper.deployProxyAsync(); - await erc721Wrapper.setBalancesAndAllowancesAsync(); - const erc721Balances = await erc721Wrapper.getBalancesAsync(); - erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address]; - erc721TakerAssetIds = erc721Balances[takerAddress][erc721Token.address]; - - exchange = await ExchangeContract.deployFrom0xArtifactAsync( - artifacts.Exchange, - provider, - txDefaults, - assetProxyUtils.encodeERC20ProxyData(zrxToken.address), - ); - exchangeWrapper = new ExchangeWrapper(exchange, provider); - await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner); - await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC721, erc721Proxy.address, owner); - - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - - defaultMakerAssetAddress = erc20EighteenDecimalTokenA.address; - defaultTakerAssetAddress = erc20EighteenDecimalTokenB.address; - - newOrderFactory = new NewOrderFactory( - usedAddresses, - zrxToken.address, - [erc20EighteenDecimalTokenA.address, erc20EighteenDecimalTokenB.address], - [erc20FiveDecimalTokenA.address, erc20FiveDecimalTokenB.address], - erc721Token, - erc721Balances, - exchange.address, - ); - }); beforeEach(async () => { await blockchainLifecycle.startAsync(); }); afterEach(async () => { await blockchainLifecycle.revertAsync(); }); - describe.only('Fill order', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); + const test = (orderScenarios: OrderScenario[]) => { + _.forEach(orderScenarios, orderScenario => { + const description = `Combinatorial OrderFill: ${orderScenario.feeRecipientScenario} ${ + orderScenario.makerAssetAmountScenario + } ${orderScenario.takerAssetAmountScenario} ${orderScenario.makerFeeScenario} ${ + orderScenario.takerFeeScenario + } ${orderScenario.expirationTimeSecondsScenario} ${orderScenario.makerAssetDataScenario} ${ + orderScenario.takerAssetDataScenario + }`; + it(description, async () => { + const order = coreCombinatorialUtils.orderFactory.generateOrder(orderScenario); + await coreCombinatorialUtils.testFillOrderScenarioAsync(order, provider); + }); }); - it('should transfer the correct amounts when makerAssetAmount === takerAssetAmount', async () => { - const order = newOrderFactory.generateOrder( - FeeRecipientAddressScenario.EthUserAddress, - OrderAmountScenario.NonZero, - OrderAmountScenario.NonZero, - OrderAmountScenario.Zero, - OrderAmountScenario.Zero, - ExpirationTimeSecondsScenario.InFuture, - AssetDataScenario.ERC20NonZRXEighteenDecimals, - AssetDataScenario.ERC20NonZRXEighteenDecimals, - ); - - // TODO: Permute signature types + }; - // TODO: Sign order (for now simply ECSign) - const orderHashBuff = orderHashUtils.getOrderHashBuff(order); - const privateKey = constants.TESTRPC_PRIVATE_KEYS[usedAddresses.indexOf(makerAddress)]; - const signature = signingUtils.signMessage(orderHashBuff, privateKey, SignatureType.EthSign); - const signedOrder = { - ...order, - signature: `0x${signature.toString('hex')}`, - }; - console.log('signedOrder', signedOrder); + const allOrderScenarios = CoreCombinatorialUtils.generateOrderCombinations(); - // TODO: Get orderRelevantState - const orderInfoUtils = new OrderInfoUtils(exchange, erc20Wrapper, zrxToken.address); - // 1. How much of this order can I fill? - const fillableTakerAssetAmount = await orderInfoUtils.getFillableTakerAssetAmountAsync( - signedOrder, - takerAddress, - ); - console.log('fillableTakerAssetAmount', fillableTakerAssetAmount); - - // TODO: Decide how much to fill (all, some) - const takerFillAmount = fillableTakerAssetAmount.div(2); // some for now - - // 2. If I fill it by X, what are the resulting balances/allowances/filled amounts expected? - // NOTE: we can't use orderStateUtils for this :( We need to do this ourselves. - - // This doesn't include taker balance/allowance checks... - /* - Inputs: - - signedOrder - - takerAddress - Outputs: - - Check fillable amount - - maker token balance & allowance - - maker ZRX balance & allowance - - taker token balance & allowance - - taker ZRX balance & allowance - Test: - - If fillable >= fillAmount: - - check that filled by fillAmount - - check makerBalance - */ - - // signedOrder = orderFactory.newSignedOrder({ - // makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - // takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - // }); ); - // - // const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - // orderHashUtils.getOrderHashHex(signedOrder), - // ); - // expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); - // - // const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - // await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); - // - // const makerAmountBoughtAfter = await exchangeWrapper.getTakerAssetFilledAmountAsync( - // orderHashUtils.getOrderHashHex(signedOrder), - // ); - // expect(makerAmountBoughtAfter).to.be.bignumber.equal(takerAssetFillAmount); - // - // const newBalances = await erc20Wrapper.getBalancesAsync(); - // - // const makerAssetFilledAmount = takerAssetFillAmount - // .times(signedOrder.makerAssetAmount) - // .dividedToIntegerBy(signedOrder.takerAssetAmount); - // const makerFeePaid = signedOrder.makerFee - // .times(makerAssetFilledAmount) - // .dividedToIntegerBy(signedOrder.makerAssetAmount); - // const takerFeePaid = signedOrder.takerFee - // .times(makerAssetFilledAmount) - // .dividedToIntegerBy(signedOrder.makerAssetAmount); - // expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - // erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount), - // ); - // expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - // erc20Balances[makerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), - // ); - // expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - // erc20Balances[makerAddress][zrxToken.address].minus(makerFeePaid), - // ); - // expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - // erc20Balances[takerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), - // ); - // expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - // erc20Balances[takerAddress][defaultMakerAssetAddress].add(makerAssetFilledAmount), - // ); - // expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - // erc20Balances[takerAddress][zrxToken.address].minus(takerFeePaid), - // ); - // expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - // erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), - // ); - }); - }); + describe.only('Fills orders', () => test(allOrderScenarios)); }); diff --git a/packages/order-utils/src/exchange_transfer_simulator.ts b/packages/order-utils/src/exchange_transfer_simulator.ts index 32d53d6a2..cac4af243 100644 --- a/packages/order-utils/src/exchange_transfer_simulator.ts +++ b/packages/order-utils/src/exchange_transfer_simulator.ts @@ -1,7 +1,8 @@ -import { ExchangeContractErrs } from '@0xproject/types'; +import { AssetProxyId, ExchangeContractErrs } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { AbstractBalanceAndProxyAllowanceLazyStore } from './abstract/abstract_balance_and_proxy_allowance_lazy_store'; +import { assetProxyUtils } from './asset_proxy_utils'; import { constants } from './constants'; import { TradeSide, TransferType } from './types'; @@ -89,8 +90,13 @@ export class ExchangeTransferSimulator { userAddress: string, amountInBaseUnits: BigNumber, ): Promise { + const assetProxyId = assetProxyUtils.decodeAssetDataId(assetData); + const isERC721Asset = assetProxyId === AssetProxyId.ERC721; const proxyAllowance = await this._store.getProxyAllowanceAsync(assetData, userAddress); - if (!proxyAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) { + // HACK: This code assumes that all tokens with an UNLIMITED_ALLOWANCE_IN_BASE_UNITS set, + // are UnlimitedAllowanceTokens. This is however not true, it just so happens that all + // DummyERC20Tokens we use in tests are. + if (!proxyAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS) && !isERC721Asset) { this._store.setProxyAllowance(assetData, userAddress, proxyAllowance.minus(amountInBaseUnits)); } } diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts index 40f235da7..dbb65de59 100644 --- a/packages/order-utils/src/order_state_utils.ts +++ b/packages/order-utils/src/order_state_utils.ts @@ -7,43 +7,85 @@ import { SignedOrder, } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; -import { assetProxyUtils } from './asset_proxy_utils'; +import { constants } from './constants'; import { orderHashUtils } from './order_hash'; import { RemainingFillableCalculator } from './remaining_fillable_calculator'; +interface SidedOrderRelevantState { + isMakerSide: boolean; + traderBalance: BigNumber; + traderProxyAllowance: BigNumber; + traderFeeBalance: BigNumber; + traderFeeProxyAllowance: BigNumber; + filledTakerAssetAmount: BigNumber; + remainingFillableAssetAmount: BigNumber; +} + const ACCEPTABLE_RELATIVE_ROUNDING_ERROR = 0.0001; export class OrderStateUtils { private _balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher; private _orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher; - private static _validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void { - const availableTakerAssetAmount = signedOrder.takerAssetAmount.minus(orderRelevantState.filledTakerAssetAmount); + private static _validateIfOrderIsValid( + signedOrder: SignedOrder, + sidedOrderRelevantState: SidedOrderRelevantState, + ): void { + const isMakerSide = sidedOrderRelevantState.isMakerSide; + const availableTakerAssetAmount = signedOrder.takerAssetAmount.minus( + sidedOrderRelevantState.filledTakerAssetAmount, + ); if (availableTakerAssetAmount.eq(0)) { throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); } - if (orderRelevantState.makerBalance.eq(0)) { - throw new Error(ExchangeContractErrs.InsufficientMakerBalance); + if (sidedOrderRelevantState.traderBalance.eq(0)) { + throw new Error( + isMakerSide + ? ExchangeContractErrs.InsufficientMakerBalance + : ExchangeContractErrs.InsufficientTakerBalance, + ); } - if (orderRelevantState.makerProxyAllowance.eq(0)) { - throw new Error(ExchangeContractErrs.InsufficientMakerAllowance); + if (sidedOrderRelevantState.traderProxyAllowance.eq(0)) { + throw new Error( + isMakerSide + ? ExchangeContractErrs.InsufficientMakerAllowance + : ExchangeContractErrs.InsufficientTakerAllowance, + ); } if (!signedOrder.makerFee.eq(0)) { - if (orderRelevantState.makerFeeBalance.eq(0)) { - throw new Error(ExchangeContractErrs.InsufficientMakerFeeBalance); + if (sidedOrderRelevantState.traderFeeBalance.eq(0)) { + throw new Error( + isMakerSide + ? ExchangeContractErrs.InsufficientMakerFeeBalance + : ExchangeContractErrs.InsufficientTakerFeeBalance, + ); } - if (orderRelevantState.makerFeeProxyAllowance.eq(0)) { - throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance); + if (sidedOrderRelevantState.traderFeeProxyAllowance.eq(0)) { + throw new Error( + isMakerSide + ? ExchangeContractErrs.InsufficientMakerFeeAllowance + : ExchangeContractErrs.InsufficientTakerFeeAllowance, + ); } } - const minFillableTakerAssetAmountWithinNoRoundingErrorRange = signedOrder.takerAssetAmount - .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR) - .dividedBy(signedOrder.makerAssetAmount); + + let minFillableTakerAssetAmountWithinNoRoundingErrorRange; + if (isMakerSide) { + minFillableTakerAssetAmountWithinNoRoundingErrorRange = signedOrder.takerAssetAmount + .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR) + .dividedBy(signedOrder.makerAssetAmount); + } else { + minFillableTakerAssetAmountWithinNoRoundingErrorRange = signedOrder.makerAssetAmount + .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR) + .dividedBy(signedOrder.takerAssetAmount); + } + if ( - orderRelevantState.remainingFillableTakerAssetAmount.lessThan( + sidedOrderRelevantState.remainingFillableAssetAmount.lessThan( minFillableTakerAssetAmountWithinNoRoundingErrorRange, ) ) { @@ -57,11 +99,20 @@ export class OrderStateUtils { this._balanceAndProxyAllowanceFetcher = balanceAndProxyAllowanceFetcher; this._orderFilledCancelledFetcher = orderFilledCancelledFetcher; } - public async getOrderStateAsync(signedOrder: SignedOrder): Promise { - const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder); + public async getOpenOrderStateAsync(signedOrder: SignedOrder): Promise { + const orderRelevantState = await this.getOpenOrderRelevantStateAsync(signedOrder); const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + const sidedOrderRelevantState = { + isMakerSide: true, + traderBalance: orderRelevantState.makerBalance, + traderProxyAllowance: orderRelevantState.makerProxyAllowance, + traderFeeBalance: orderRelevantState.makerFeeBalance, + traderFeeProxyAllowance: orderRelevantState.makerFeeProxyAllowance, + filledTakerAssetAmount: orderRelevantState.filledTakerAssetAmount, + remainingFillableAssetAmount: orderRelevantState.remainingFillableMakerAssetAmount, + }; try { - OrderStateUtils._validateIfOrderIsValid(signedOrder, orderRelevantState); + OrderStateUtils._validateIfOrderIsValid(signedOrder, sidedOrderRelevantState); const orderState: OrderStateValid = { isValid: true, orderHash, @@ -77,63 +128,152 @@ export class OrderStateUtils { return orderState; } } - public async getOrderRelevantStateAsync(signedOrder: SignedOrder): Promise { - const zrxTokenAddress = this._orderFilledCancelledFetcher.getZRXTokenAddress(); - const makerProxyData = assetProxyUtils.decodeERC20AssetData(signedOrder.makerAssetData); - const makerAssetAddress = makerProxyData.tokenAddress; - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - const makerBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync( - makerAssetAddress, - signedOrder.makerAddress, + public async getOpenOrderRelevantStateAsync(signedOrder: SignedOrder): Promise { + const isMaker = true; + const sidedOrderRelevantState = await this._getSidedOrderRelevantStateAsync( + isMaker, + signedOrder, + signedOrder.takerAddress, + ); + const remainingFillableTakerAssetAmount = sidedOrderRelevantState.remainingFillableAssetAmount + .times(signedOrder.takerAssetAmount) + .dividedToIntegerBy(signedOrder.makerAssetAmount); + + const orderRelevantState = { + makerBalance: sidedOrderRelevantState.traderBalance, + makerProxyAllowance: sidedOrderRelevantState.traderProxyAllowance, + makerFeeBalance: sidedOrderRelevantState.traderFeeBalance, + makerFeeProxyAllowance: sidedOrderRelevantState.traderFeeProxyAllowance, + filledTakerAssetAmount: sidedOrderRelevantState.filledTakerAssetAmount, + remainingFillableMakerAssetAmount: sidedOrderRelevantState.remainingFillableAssetAmount, + remainingFillableTakerAssetAmount, + }; + return orderRelevantState; + } + public async getMaxFillableTakerAssetAmountAsync( + signedOrder: SignedOrder, + takerAddress: string, + ): Promise { + // Get max fillable amount for an order, considering the makers ability to fill + let isMaker = true; + const orderRelevantMakerState = await this._getSidedOrderRelevantStateAsync( + isMaker, + signedOrder, + signedOrder.takerAddress, + ); + const remainingFillableTakerAssetAmountGivenMakersStatus = orderRelevantMakerState.remainingFillableAssetAmount; + + // Get max fillable amount for an order, considering the takers ability to fill + isMaker = false; + const orderRelevantTakerState = await this._getSidedOrderRelevantStateAsync(isMaker, signedOrder, takerAddress); + const remainingFillableTakerAssetAmountGivenTakersStatus = orderRelevantTakerState.remainingFillableAssetAmount; + + // The min of these two in the actualy max fillable by either party + const fillableTakerAssetAmount = BigNumber.min([ + remainingFillableTakerAssetAmountGivenMakersStatus, + remainingFillableTakerAssetAmountGivenTakersStatus, + ]); + + return fillableTakerAssetAmount; + } + public async getMaxFillableTakerAssetAmountForFailingOrderAsync( + signedOrder: SignedOrder, + takerAddress: string, + ): Promise { + // Get min of taker balance & allowance + const takerAssetBalanceOfTaker = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync( + signedOrder.takerAssetData, + takerAddress, ); - const makerProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync( - makerAssetAddress, - signedOrder.makerAddress, + const takerAssetAllowanceOfTaker = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync( + signedOrder.takerAssetData, + takerAddress, ); - const makerFeeBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync( - zrxTokenAddress, - signedOrder.makerAddress, + const minTakerAssetAmount = BigNumber.min([takerAssetBalanceOfTaker, takerAssetAllowanceOfTaker]); + + // get remainingFillAmount + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + const filledTakerAssetAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash); + const remainingFillTakerAssetAmount = signedOrder.takerAssetAmount.minus(filledTakerAssetAmount); + + if (minTakerAssetAmount.gte(remainingFillTakerAssetAmount)) { + return remainingFillTakerAssetAmount; + } else { + return minTakerAssetAmount; + } + } + private async _getSidedOrderRelevantStateAsync( + isMakerSide: boolean, + signedOrder: SignedOrder, + takerAddress: string, + ): Promise { + let traderAddress; + let assetData; + let assetAmount; + let feeAmount; + if (isMakerSide) { + traderAddress = signedOrder.makerAddress; + assetData = signedOrder.makerAssetData; + assetAmount = signedOrder.makerAssetAmount; + feeAmount = signedOrder.makerFee; + } else { + traderAddress = takerAddress; + assetData = signedOrder.takerAssetData; + assetAmount = signedOrder.takerAssetAmount; + feeAmount = signedOrder.takerFee; + } + const zrxAssetData = this._orderFilledCancelledFetcher.getZRXAssetData(); + const isAssetZRX = assetData === zrxAssetData; + + const traderBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(assetData, traderAddress); + const traderProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync( + assetData, + traderAddress, + ); + const traderFeeBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync( + zrxAssetData, + traderAddress, ); - const makerFeeProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync( - zrxTokenAddress, - signedOrder.makerAddress, + const traderFeeProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync( + zrxAssetData, + traderAddress, ); + + const transferrableTraderAssetAmount = BigNumber.min([traderProxyAllowance, traderBalance]); + const transferrableFeeAssetAmount = BigNumber.min([traderFeeProxyAllowance, traderFeeBalance]); + + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); const filledTakerAssetAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash); - const isOrderCancelled = await this._orderFilledCancelledFetcher.isOrderCancelledAsync(orderHash); const totalMakerAssetAmount = signedOrder.makerAssetAmount; const totalTakerAssetAmount = signedOrder.takerAssetAmount; + const isOrderCancelled = await this._orderFilledCancelledFetcher.isOrderCancelledAsync(orderHash); const remainingTakerAssetAmount = isOrderCancelled ? new BigNumber(0) : totalTakerAssetAmount.minus(filledTakerAssetAmount); - const remainingMakerAssetAmount = remainingTakerAssetAmount - .times(totalMakerAssetAmount) - .dividedToIntegerBy(totalTakerAssetAmount); - const transferrableMakerAssetAmount = BigNumber.min([makerProxyAllowance, makerBalance]); - const transferrableFeeAssetAmount = BigNumber.min([makerFeeProxyAllowance, makerFeeBalance]); - - const zrxAssetData = assetProxyUtils.encodeERC20AssetData(zrxTokenAddress); - const isMakerAssetZRX = signedOrder.makerAssetData === zrxAssetData; + const remainingMakerAssetAmount = remainingTakerAssetAmount.eq(0) + ? new BigNumber(0) + : remainingTakerAssetAmount.times(totalMakerAssetAmount).dividedToIntegerBy(totalTakerAssetAmount); + const remainingAssetAmount = isMakerSide ? remainingMakerAssetAmount : remainingTakerAssetAmount; + const remainingFillableCalculator = new RemainingFillableCalculator( - signedOrder.makerFee, - signedOrder.makerAssetAmount, - isMakerAssetZRX, - transferrableMakerAssetAmount, + feeAmount, + assetAmount, + isAssetZRX, + transferrableTraderAssetAmount, transferrableFeeAssetAmount, - remainingMakerAssetAmount, + remainingAssetAmount, ); - const remainingFillableMakerAssetAmount = remainingFillableCalculator.computeRemainingFillable(); - const remainingFillableTakerAssetAmount = remainingFillableMakerAssetAmount - .times(signedOrder.takerAssetAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - const orderRelevantState = { - makerBalance, - makerProxyAllowance, - makerFeeBalance, - makerFeeProxyAllowance, + const remainingFillableAssetAmount = remainingFillableCalculator.computeRemainingFillable(); + + const sidedOrderRelevantState = { + isMakerSide, + traderBalance, + traderProxyAllowance, + traderFeeBalance, + traderFeeProxyAllowance, filledTakerAssetAmount, - remainingFillableMakerAssetAmount, - remainingFillableTakerAssetAmount, + remainingFillableAssetAmount, }; - return orderRelevantState; + return sidedOrderRelevantState; } } -- cgit From 45186b70ec446a42736dd99b947463b1e1569b59 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 13 Jun 2018 16:14:48 +0200 Subject: Move orderFill combinatorialTests to exchange folder --- packages/contracts/test/combinatorial_tests.ts | 48 ---------------------- .../contracts/test/exchange/combinatorial_tests.ts | 48 ++++++++++++++++++++++ 2 files changed, 48 insertions(+), 48 deletions(-) delete mode 100644 packages/contracts/test/combinatorial_tests.ts create mode 100644 packages/contracts/test/exchange/combinatorial_tests.ts diff --git a/packages/contracts/test/combinatorial_tests.ts b/packages/contracts/test/combinatorial_tests.ts deleted file mode 100644 index c7de8d7e3..000000000 --- a/packages/contracts/test/combinatorial_tests.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import * as _ from 'lodash'; - -import { chaiSetup } from '../src/utils/chai_setup'; -import { CoreCombinatorialUtils, coreCombinatorialUtilsFactoryAsync } from '../src/utils/core_combinatorial_utils'; -import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper'; - -import { OrderScenario } from '../src/utils/types'; - -chaiSetup.configure(); -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('Combinatorial tests', () => { - let coreCombinatorialUtils: CoreCombinatorialUtils; - - before(async () => { - await blockchainLifecycle.startAsync(); - coreCombinatorialUtils = await coreCombinatorialUtilsFactoryAsync(web3Wrapper, txDefaults); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - const test = (orderScenarios: OrderScenario[]) => { - _.forEach(orderScenarios, orderScenario => { - const description = `Combinatorial OrderFill: ${orderScenario.feeRecipientScenario} ${ - orderScenario.makerAssetAmountScenario - } ${orderScenario.takerAssetAmountScenario} ${orderScenario.makerFeeScenario} ${ - orderScenario.takerFeeScenario - } ${orderScenario.expirationTimeSecondsScenario} ${orderScenario.makerAssetDataScenario} ${ - orderScenario.takerAssetDataScenario - }`; - it(description, async () => { - const order = coreCombinatorialUtils.orderFactory.generateOrder(orderScenario); - await coreCombinatorialUtils.testFillOrderScenarioAsync(order, provider); - }); - }); - }; - - const allOrderScenarios = CoreCombinatorialUtils.generateOrderCombinations(); - - describe.only('Fills orders', () => test(allOrderScenarios)); -}); diff --git a/packages/contracts/test/exchange/combinatorial_tests.ts b/packages/contracts/test/exchange/combinatorial_tests.ts new file mode 100644 index 000000000..2870a22ed --- /dev/null +++ b/packages/contracts/test/exchange/combinatorial_tests.ts @@ -0,0 +1,48 @@ +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import * as _ from 'lodash'; + +import { chaiSetup } from '../../src/utils/chai_setup'; +import { CoreCombinatorialUtils, coreCombinatorialUtilsFactoryAsync } from '../../src/utils/core_combinatorial_utils'; +import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; + +import { OrderScenario } from '../../src/utils/types'; + +chaiSetup.configure(); +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('Combinatorial tests', () => { + let coreCombinatorialUtils: CoreCombinatorialUtils; + + before(async () => { + await blockchainLifecycle.startAsync(); + coreCombinatorialUtils = await coreCombinatorialUtilsFactoryAsync(web3Wrapper, txDefaults); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + const test = (orderScenarios: OrderScenario[]) => { + _.forEach(orderScenarios, orderScenario => { + const description = `Combinatorial OrderFill: ${orderScenario.feeRecipientScenario} ${ + orderScenario.makerAssetAmountScenario + } ${orderScenario.takerAssetAmountScenario} ${orderScenario.makerFeeScenario} ${ + orderScenario.takerFeeScenario + } ${orderScenario.expirationTimeSecondsScenario} ${orderScenario.makerAssetDataScenario} ${ + orderScenario.takerAssetDataScenario + }`; + it(description, async () => { + const order = coreCombinatorialUtils.orderFactory.generateOrder(orderScenario); + await coreCombinatorialUtils.testFillOrderScenarioAsync(order, provider); + }); + }); + }; + + const allOrderScenarios = CoreCombinatorialUtils.generateOrderCombinations(); + + describe.only('Fills orders', () => test(allOrderScenarios)); +}); -- cgit From 6239686afaf733707ad0c4687c535969c2d53205 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 13 Jun 2018 18:36:30 +0200 Subject: Fix divide by zero bug --- packages/order-utils/src/remaining_fillable_calculator.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/order-utils/src/remaining_fillable_calculator.ts b/packages/order-utils/src/remaining_fillable_calculator.ts index bc146e931..29e19e5ab 100644 --- a/packages/order-utils/src/remaining_fillable_calculator.ts +++ b/packages/order-utils/src/remaining_fillable_calculator.ts @@ -23,9 +23,9 @@ export class RemainingFillableCalculator { this._transferrableAssetAmount = transferrableAssetAmount; this._transferrableFeeAmount = transferrableFeeAmount; this._remainingOrderAssetAmount = remainingOrderAssetAmount; - this._remainingOrderFeeAmount = remainingOrderAssetAmount - .times(this._orderFee) - .dividedToIntegerBy(this._orderAssetAmount); + this._remainingOrderFeeAmount = orderAssetAmount.eq(0) + ? new BigNumber(0) + : remainingOrderAssetAmount.times(orderFee).dividedToIntegerBy(orderAssetAmount); } public computeRemainingFillable(): BigNumber { if (this._hasSufficientFundsForFeeAndTransferAmount()) { -- cgit From 98405a39dbb870c1eb9f562afca4539602917c67 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 14 Jun 2018 10:40:17 +0200 Subject: Add ability to specify takerAssetFillAmount and taker scenarios as part of a FillScenario --- .../src/utils/core_combinatorial_utils.ts | 79 +++++++++++++++------- packages/contracts/src/utils/new_order_factory.ts | 69 +++++++++++++++++-- packages/contracts/src/utils/types.ts | 22 +++++- .../contracts/test/exchange/combinatorial_tests.ts | 18 +++-- 4 files changed, 153 insertions(+), 35 deletions(-) diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index 6fbd8304e..dac4e9edd 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -7,7 +7,7 @@ import { OrderValidationUtils, } from '@0xproject/order-utils'; import { AssetProxyId, Order, SignatureType, SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; +import { BigNumber, errorUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; import { BlockParamLiteral, LogWithDecodedArgs, Provider, TxData } from 'ethereum-types'; @@ -35,6 +35,9 @@ import { FeeRecipientAddressScenario, OrderAmountScenario, OrderScenario, + TakerAssetFillAmountScenario, + TakerScenario, + FillScenario, } from '../utils/types'; chaiSetup.configure(); @@ -142,11 +145,12 @@ export class CoreCombinatorialUtils { public exchangeWrapper: ExchangeWrapper; public assetWrapper: AssetWrapper; public static generateOrderCombinations(): OrderScenario[] { + const takerScenarios = [TakerScenario.Unspecified]; const feeRecipientScenarios = [FeeRecipientAddressScenario.EthUserAddress]; - const makerAssetAmountScenario = [OrderAmountScenario.NonZero]; - const takerAssetAmountScenario = [OrderAmountScenario.NonZero]; - const makerFeeScenario = [OrderAmountScenario.NonZero]; - const takerFeeScenario = [OrderAmountScenario.NonZero]; + const makerAssetAmountScenario = [OrderAmountScenario.Large]; + const takerAssetAmountScenario = [OrderAmountScenario.Large]; + const makerFeeScenario = [OrderAmountScenario.Large]; + const takerFeeScenario = [OrderAmountScenario.Large]; const expirationTimeSecondsScenario = [ExpirationTimeSecondsScenario.InFuture]; const makerAssetDataScenario = [ AssetDataScenario.ERC20FiveDecimals, @@ -161,6 +165,7 @@ export class CoreCombinatorialUtils { AssetDataScenario.ZRXFeeToken, ]; const orderScenarioArrays = CoreCombinatorialUtils._allPossibleCases([ + takerScenarios, feeRecipientScenarios, makerAssetAmountScenario, takerAssetAmountScenario, @@ -173,14 +178,15 @@ export class CoreCombinatorialUtils { const orderScenarios = _.map(orderScenarioArrays, orderScenarioArray => { const orderScenario: OrderScenario = { - feeRecipientScenario: orderScenarioArray[0] as FeeRecipientAddressScenario, - makerAssetAmountScenario: orderScenarioArray[1] as OrderAmountScenario, - takerAssetAmountScenario: orderScenarioArray[2] as OrderAmountScenario, - makerFeeScenario: orderScenarioArray[3] as OrderAmountScenario, - takerFeeScenario: orderScenarioArray[4] as OrderAmountScenario, - expirationTimeSecondsScenario: orderScenarioArray[5] as ExpirationTimeSecondsScenario, - makerAssetDataScenario: orderScenarioArray[6] as AssetDataScenario, - takerAssetDataScenario: orderScenarioArray[7] as AssetDataScenario, + takerScenario: orderScenarioArray[0] as TakerScenario, + feeRecipientScenario: orderScenarioArray[1] as FeeRecipientAddressScenario, + makerAssetAmountScenario: orderScenarioArray[2] as OrderAmountScenario, + takerAssetAmountScenario: orderScenarioArray[3] as OrderAmountScenario, + makerFeeScenario: orderScenarioArray[4] as OrderAmountScenario, + takerFeeScenario: orderScenarioArray[5] as OrderAmountScenario, + expirationTimeSecondsScenario: orderScenarioArray[6] as ExpirationTimeSecondsScenario, + makerAssetDataScenario: orderScenarioArray[7] as AssetDataScenario, + takerAssetDataScenario: orderScenarioArray[8] as AssetDataScenario, }; return orderScenario; }); @@ -225,7 +231,10 @@ export class CoreCombinatorialUtils { this.exchangeWrapper = exchangeWrapper; this.assetWrapper = assetWrapper; } - public async testFillOrderScenarioAsync(order: Order, provider: Provider): Promise { + public async testFillOrderScenarioAsync(provider: Provider, fillScenario: FillScenario): Promise { + // 1. Generate order + const order = this.orderFactory.generateOrder(fillScenario.orderScenario); + // 2. Sign order const orderHashBuff = orderHashUtils.getOrderHashBuff(order); const signature = signingUtils.signMessage(orderHashBuff, this.makerPrivateKey, SignatureType.EthSign); @@ -235,8 +244,9 @@ export class CoreCombinatorialUtils { }; // 3. Permutate the maker and taker balance/allowance scenarios + // TODO(fabio) - // 4. Figure out fill amount OR error + // 4. Figure out fill amount const balanceAndProxyAllowanceFetcher = new SimpleAssetBalanceAndProxyAllowanceFetcher(this.assetWrapper); const orderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher( this.exchangeWrapper, @@ -249,14 +259,37 @@ export class CoreCombinatorialUtils { this.takerAddress, ); - // If order is fillable, decide how much to fill - // TODO: Make this configurable - const takerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.takerAssetData); - const makerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.makerAssetData); - const isEitherAssetERC721 = takerAssetProxyId === ERC721_PROXY_ID || makerAssetProxyId === ERC721_PROXY_ID; - const takerAssetFillAmount = isEitherAssetERC721 - ? fillableTakerAssetAmount - : fillableTakerAssetAmount.div(2).floor(); + let takerAssetFillAmount; + const takerAssetFillAmountScenario = fillScenario.takerAssetFillAmountScenario; + switch (takerAssetFillAmountScenario) { + case TakerAssetFillAmountScenario.Zero: + takerAssetFillAmount = new BigNumber(0); + break; + + case TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount: + takerAssetFillAmount = fillableTakerAssetAmount; + break; + + case TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount: + takerAssetFillAmount = fillableTakerAssetAmount.add(1); + break; + + case TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount: + const takerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.takerAssetData); + const makerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.makerAssetData); + const isEitherAssetERC721 = + takerAssetProxyId === ERC721_PROXY_ID || makerAssetProxyId === ERC721_PROXY_ID; + if (isEitherAssetERC721) { + throw new Error( + 'Cannot test `TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount` together with ERC721 assets since orders involving ERC721 must always be filled exactly.', + ); + } + takerAssetFillAmount = fillableTakerAssetAmount.div(2).floor(); + break; + + default: + throw errorUtils.spawnSwitchErr('TakerAssetFillAmountScenario', takerAssetFillAmountScenario); + } // 5. If I fill it by X, what are the resulting balances/allowances/filled amounts exp? const orderValidationUtils = new OrderValidationUtils(orderFilledCancelledFetcher); diff --git a/packages/contracts/src/utils/new_order_factory.ts b/packages/contracts/src/utils/new_order_factory.ts index 715e38d6d..7de7be3bd 100644 --- a/packages/contracts/src/utils/new_order_factory.ts +++ b/packages/contracts/src/utils/new_order_factory.ts @@ -12,11 +12,15 @@ import { FeeRecipientAddressScenario, OrderAmountScenario, OrderScenario, + TakerScenario, } from './types'; const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10000000000000000000); +const FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(5000000000000000000); const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100000000000000000); +const POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(50000000000000000); const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1000000); +const FIVE_UNITS_FIVE_DECIMALS = new BigNumber(500000); const ONE_NFT_UNIT = new BigNumber(1); export class NewOrderFactory { @@ -46,7 +50,7 @@ export class NewOrderFactory { } public generateOrder(orderScenario: OrderScenario): Order { const makerAddress = this._userAddresses[1]; - const takerAddress = this._userAddresses[2]; + let takerAddress = this._userAddresses[2]; const erc721MakerAssetIds = this._erc721Balances[makerAddress][this._erc721Token.address]; const erc721TakerAssetIds = this._erc721Balances[takerAddress][this._erc721Token.address]; let feeRecipientAddress; @@ -114,7 +118,7 @@ export class NewOrderFactory { } switch (orderScenario.makerAssetAmountScenario) { - case OrderAmountScenario.NonZero: + case OrderAmountScenario.Large: switch (orderScenario.makerAssetDataScenario) { case AssetDataScenario.ZRXFeeToken: case AssetDataScenario.ERC20NonZRXEighteenDecimals: @@ -130,6 +134,22 @@ export class NewOrderFactory { throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); } break; + case OrderAmountScenario.Small: + switch (orderScenario.makerAssetDataScenario) { + case AssetDataScenario.ZRXFeeToken: + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + makerAssetAmount = FIVE_UNITS_EIGHTEEN_DECIMALS; + break; + case AssetDataScenario.ERC20FiveDecimals: + makerAssetAmount = FIVE_UNITS_FIVE_DECIMALS; + break; + case AssetDataScenario.ERC721: + makerAssetAmount = ONE_NFT_UNIT; + break; + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); + } + break; case OrderAmountScenario.Zero: makerAssetAmount = new BigNumber(0); break; @@ -138,7 +158,7 @@ export class NewOrderFactory { } switch (orderScenario.takerAssetAmountScenario) { - case OrderAmountScenario.NonZero: + case OrderAmountScenario.Large: switch (orderScenario.takerAssetDataScenario) { case AssetDataScenario.ERC20NonZRXEighteenDecimals: case AssetDataScenario.ZRXFeeToken: @@ -154,6 +174,22 @@ export class NewOrderFactory { throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); } break; + case OrderAmountScenario.Small: + switch (orderScenario.takerAssetDataScenario) { + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + case AssetDataScenario.ZRXFeeToken: + takerAssetAmount = FIVE_UNITS_EIGHTEEN_DECIMALS; + break; + case AssetDataScenario.ERC20FiveDecimals: + takerAssetAmount = FIVE_UNITS_FIVE_DECIMALS; + break; + case AssetDataScenario.ERC721: + takerAssetAmount = ONE_NFT_UNIT; + break; + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); + } + break; case OrderAmountScenario.Zero: takerAssetAmount = new BigNumber(0); break; @@ -162,9 +198,12 @@ export class NewOrderFactory { } switch (orderScenario.makerFeeScenario) { - case OrderAmountScenario.NonZero: + case OrderAmountScenario.Large: makerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; break; + case OrderAmountScenario.Small: + makerFee = POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS; + break; case OrderAmountScenario.Zero: makerFee = new BigNumber(0); break; @@ -173,9 +212,12 @@ export class NewOrderFactory { } switch (orderScenario.takerFeeScenario) { - case OrderAmountScenario.NonZero: + case OrderAmountScenario.Large: takerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; break; + case OrderAmountScenario.Small: + takerFee = POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS; + break; case OrderAmountScenario.Zero: takerFee = new BigNumber(0); break; @@ -197,6 +239,23 @@ export class NewOrderFactory { ); } + switch (orderScenario.takerScenario) { + case TakerScenario.CorrectlySpecified: + break; // noop since takerAddress is already specified + + case TakerScenario.IncorrectlySpecified: + const notTaker = this._userAddresses[3]; + takerAddress = notTaker; + break; + + case TakerScenario.Unspecified: + takerAddress = constants.NULL_ADDRESS; + break; + + default: + throw errorUtils.spawnSwitchErr('TakerScenario', orderScenario.takerScenario); + } + const order: Order = { senderAddress: constants.NULL_ADDRESS, makerAddress, diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 77e8d867e..273e8c2d6 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -160,7 +160,14 @@ export enum FeeRecipientAddressScenario { export enum OrderAmountScenario { Zero = 'ZERO', - NonZero = 'NON_ZERO', + Large = 'LARGE', + Small = 'SMALL', +} + +export enum TakerScenario { + CorrectlySpecified = 'CORRECTLY_SPECFIED', + IncorrectlySpecified = 'INCORRECTLY_SPECFIED', + Unspecified = 'UNSPECIFIED', } export enum ExpirationTimeSecondsScenario { @@ -175,7 +182,15 @@ export enum AssetDataScenario { ERC20NonZRXEighteenDecimals = 'ERC20_NON_ZRX_EIGHTEEN_DECIMALS', } +export enum TakerAssetFillAmountScenario { + Zero = 'ZERO', + GreaterThanRemainingFillableTakerAssetAmount = 'GREATER_THAN_REMAINING_FILLABLE_TAKER_ASSET_AMOUNT', + LessThanRemainingFillableTakerAssetAmount = 'GREATER_THAN_REMAINING_FILLABLE_TAKER_ASSET_AMOUNT', + ExactlyRemainingFillableTakerAssetAmount = 'GREATER_THAN_REMAINING_FILLABLE_TAKER_ASSET_AMOUNT', +} + export interface OrderScenario { + takerScenario: TakerScenario; feeRecipientScenario: FeeRecipientAddressScenario; makerAssetAmountScenario: OrderAmountScenario; takerAssetAmountScenario: OrderAmountScenario; @@ -185,3 +200,8 @@ export interface OrderScenario { makerAssetDataScenario: AssetDataScenario; takerAssetDataScenario: AssetDataScenario; } + +export interface FillScenario { + orderScenario: OrderScenario; + takerAssetFillAmountScenario: TakerAssetFillAmountScenario; +} diff --git a/packages/contracts/test/exchange/combinatorial_tests.ts b/packages/contracts/test/exchange/combinatorial_tests.ts index 2870a22ed..fd9193d69 100644 --- a/packages/contracts/test/exchange/combinatorial_tests.ts +++ b/packages/contracts/test/exchange/combinatorial_tests.ts @@ -5,7 +5,7 @@ import { chaiSetup } from '../../src/utils/chai_setup'; import { CoreCombinatorialUtils, coreCombinatorialUtilsFactoryAsync } from '../../src/utils/core_combinatorial_utils'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; -import { OrderScenario } from '../../src/utils/types'; +import { FillScenario, OrderScenario, TakerAssetFillAmountScenario } from '../../src/utils/types'; chaiSetup.configure(); const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); @@ -26,8 +26,9 @@ describe('Combinatorial tests', () => { afterEach(async () => { await blockchainLifecycle.revertAsync(); }); - const test = (orderScenarios: OrderScenario[]) => { - _.forEach(orderScenarios, orderScenario => { + const test = (fillScenarios: FillScenario[]) => { + _.forEach(fillScenarios, fillScenario => { + const orderScenario = fillScenario.orderScenario; const description = `Combinatorial OrderFill: ${orderScenario.feeRecipientScenario} ${ orderScenario.makerAssetAmountScenario } ${orderScenario.takerAssetAmountScenario} ${orderScenario.makerFeeScenario} ${ @@ -36,13 +37,18 @@ describe('Combinatorial tests', () => { orderScenario.takerAssetDataScenario }`; it(description, async () => { - const order = coreCombinatorialUtils.orderFactory.generateOrder(orderScenario); - await coreCombinatorialUtils.testFillOrderScenarioAsync(order, provider); + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); }); }; const allOrderScenarios = CoreCombinatorialUtils.generateOrderCombinations(); + const allFillScenarios = _.map(allOrderScenarios, orderScenario => { + return { + orderScenario, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + }; + }); - describe.only('Fills orders', () => test(allOrderScenarios)); + describe('Fills orders', () => test(allFillScenarios)); }); -- cgit From d31b051fc57ead4243f52e5536ab0959e3588b4c Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 14 Jun 2018 10:41:04 +0200 Subject: Replace initial set of legacy manually written fillOrder tests with declarative FillScenario tests --- packages/contracts/test/exchange/core.ts | 434 ------------------------- packages/contracts/test/exchange/fill_order.ts | 186 +++++++++++ 2 files changed, 186 insertions(+), 434 deletions(-) create mode 100644 packages/contracts/test/exchange/fill_order.ts diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index 63c2fa6c0..193754e70 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -131,295 +131,6 @@ describe('Exchange core', () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); signedOrder = orderFactory.newSignedOrder(); }); - it('should transfer the correct amounts when makerAssetAmount === takerAssetAmount', async () => { - signedOrder = orderFactory.newSignedOrder({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - }); - - const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrder), - ); - expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); - - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); - - const makerAmountBoughtAfter = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrder), - ); - expect(makerAmountBoughtAfter).to.be.bignumber.equal(takerAssetFillAmount); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - - const makerAssetFilledAmount = takerAssetFillAmount - .times(signedOrder.makerAssetAmount) - .dividedToIntegerBy(signedOrder.takerAssetAmount); - const makerFeePaid = signedOrder.makerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - const takerFeePaid = signedOrder.takerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount), - ); - expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(makerFeePaid), - ); - expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerAssetAddress].add(makerAssetFilledAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(takerFeePaid), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), - ); - }); - - it('should transfer the correct amounts when makerAssetAmount > takerAssetAmount', async () => { - signedOrder = orderFactory.newSignedOrder({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - }); - - const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrder), - ); - expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); - - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); - - const makerAmountBoughtAfter = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrder), - ); - expect(makerAmountBoughtAfter).to.be.bignumber.equal(takerAssetFillAmount); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - - const makerAssetFilledAmount = takerAssetFillAmount - .times(signedOrder.makerAssetAmount) - .dividedToIntegerBy(signedOrder.takerAssetAmount); - const makerFeePaid = signedOrder.makerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - const takerFeePaid = signedOrder.takerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount), - ); - expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(makerFeePaid), - ); - expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerAssetAddress].add(makerAssetFilledAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(takerFeePaid), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), - ); - }); - - it('should transfer the correct amounts when makerAssetAmount < takerAssetAmount', async () => { - signedOrder = orderFactory.newSignedOrder({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), - }); - - const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrder), - ); - expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); - - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); - - const makerAmountBoughtAfter = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrder), - ); - expect(makerAmountBoughtAfter).to.be.bignumber.equal(takerAssetFillAmount); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - - const makerAssetFilledAmount = takerAssetFillAmount - .times(signedOrder.makerAssetAmount) - .dividedToIntegerBy(signedOrder.takerAssetAmount); - const makerFeePaid = signedOrder.makerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - const takerFeePaid = signedOrder.takerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount), - ); - expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(makerFeePaid), - ); - expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerAssetAddress].add(makerAssetFilledAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(takerFeePaid), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), - ); - }); - - it('should transfer the correct amounts when taker is specified and order is claimed by taker', async () => { - signedOrder = orderFactory.newSignedOrder({ - takerAddress, - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), - }); - - const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrder), - ); - expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0); - - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); - - const makerAmountBoughtAfter = await exchangeWrapper.getTakerAssetFilledAmountAsync( - orderHashUtils.getOrderHashHex(signedOrder), - ); - const expectedMakerAmountBoughtAfter = takerAssetFillAmount.add(takerAssetFilledAmountBefore); - expect(makerAmountBoughtAfter).to.be.bignumber.equal(expectedMakerAmountBoughtAfter); - - const newBalances = await erc20Wrapper.getBalancesAsync(); - - const makerAssetFilledAmount = takerAssetFillAmount - .times(signedOrder.makerAssetAmount) - .dividedToIntegerBy(signedOrder.takerAssetAmount); - const makerFeePaid = signedOrder.makerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - const takerFeePaid = signedOrder.takerFee - .times(makerAssetFilledAmount) - .dividedToIntegerBy(signedOrder.makerAssetAmount); - expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount), - ); - expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(makerFeePaid), - ); - expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerAssetAddress].add(makerAssetFilledAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(takerFeePaid), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), - ); - }); - - it('should fill remaining value if takerAssetFillAmount > remaining takerAssetAmount', async () => { - const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); - - const res = await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { - takerAssetFillAmount: signedOrder.takerAssetAmount, - }); - const log = res.logs[0] as LogWithDecodedArgs; - expect(log.args.takerAssetFilledAmount).to.be.bignumber.equal( - signedOrder.takerAssetAmount.minus(takerAssetFillAmount), - ); - const newBalances = await erc20Wrapper.getBalancesAsync(); - - expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerAssetAddress].minus(signedOrder.makerAssetAmount), - ); - expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerAssetAddress].add(signedOrder.takerAssetAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(signedOrder.makerFee), - ); - expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerAssetAddress].minus(signedOrder.takerAssetAmount), - ); - expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerAssetAddress].add(signedOrder.makerAssetAmount), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(signedOrder.takerFee), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add( - signedOrder.makerFee.add(signedOrder.takerFee), - ), - ); - }); - - it('should log 1 event with the correct arguments when order has a feeRecipient', async () => { - const divisor = 2; - const res = await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { - takerAssetFillAmount: signedOrder.takerAssetAmount.div(divisor), - }); - expect(res.logs).to.have.length(1); - - const log = res.logs[0] as LogWithDecodedArgs; - const logArgs = log.args; - const expectedFilledMakerAssetAmount = signedOrder.makerAssetAmount.div(divisor); - const expectedFilledTakerAssetAmount = signedOrder.takerAssetAmount.div(divisor); - const expectedFeeMPaid = signedOrder.makerFee.div(divisor); - const expectedFeeTPaid = signedOrder.takerFee.div(divisor); - - expect(signedOrder.makerAddress).to.be.equal(logArgs.makerAddress); - expect(takerAddress).to.be.equal(logArgs.takerAddress); - expect(signedOrder.feeRecipientAddress).to.be.equal(logArgs.feeRecipientAddress); - expect(signedOrder.makerAssetData).to.be.equal(logArgs.makerAssetData); - expect(signedOrder.takerAssetData).to.be.equal(logArgs.takerAssetData); - expect(expectedFilledMakerAssetAmount).to.be.bignumber.equal(logArgs.makerAssetFilledAmount); - expect(expectedFilledTakerAssetAmount).to.be.bignumber.equal(logArgs.takerAssetFilledAmount); - expect(expectedFeeMPaid).to.be.bignumber.equal(logArgs.makerFeePaid); - expect(expectedFeeTPaid).to.be.bignumber.equal(logArgs.takerFeePaid); - expect(orderHashUtils.getOrderHashHex(signedOrder)).to.be.equal(logArgs.orderHash); - }); - - it('should throw when taker is specified and order is claimed by other', async () => { - signedOrder = orderFactory.newSignedOrder({ - takerAddress: feeRecipientAddress, - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), - }); - return expectRevertOrAlwaysFailingTransactionAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - ); - }); - it('should throw if signature is invalid', async () => { signedOrder = orderFactory.newSignedOrder({ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), @@ -437,36 +148,6 @@ describe('Exchange core', () => { ); }); - it('should throw if makerAssetAmount is 0', async () => { - signedOrder = orderFactory.newSignedOrder({ - makerAssetAmount: new BigNumber(0), - }); - - return expectRevertOrAlwaysFailingTransactionAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - ); - }); - - it('should throw if takerAssetAmount is 0', async () => { - signedOrder = orderFactory.newSignedOrder({ - takerAssetAmount: new BigNumber(0), - }); - - return expectRevertOrAlwaysFailingTransactionAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - ); - }); - - it('should throw if takerAssetFillAmount is 0', async () => { - signedOrder = orderFactory.newSignedOrder(); - - return expectRevertOrAlwaysFailingTransactionAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { - takerAssetFillAmount: new BigNumber(0), - }), - ); - }); - it('should throw if maker erc20Balances are too low to fill order', async () => { signedOrder = orderFactory.newSignedOrder({ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18), @@ -510,15 +191,6 @@ describe('Exchange core', () => { ); }); - it('should throw if an order is expired', async () => { - signedOrder = orderFactory.newSignedOrder({ - expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)), - }); - return expectRevertOrAlwaysFailingTransactionAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - ); - }); - it('should throw if no value is filled', async () => { signedOrder = orderFactory.newSignedOrder(); await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); @@ -704,32 +376,6 @@ describe('Exchange core', () => { }); describe('Testing Exchange of ERC721 Tokens', () => { - it('should successfully exchange a single token between the maker and taker (via fillOrder)', async () => { - // Construct Exchange parameters - const makerAssetId = erc721MakerAssetIds[0]; - const takerAssetId = erc721TakerAssetIds[1]; - signedOrder = orderFactory.newSignedOrder({ - makerAssetAmount: new BigNumber(1), - takerAssetAmount: new BigNumber(1), - makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), - takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), - }); - // Verify pre-conditions - const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); - expect(initialOwnerMakerAsset).to.be.bignumber.equal(makerAddress); - const initialOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); - expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); - // Call Exchange - const takerAssetFillAmount = signedOrder.takerAssetAmount; - // tslint:disable-next-line:no-unused-variable - const res = await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); - // Verify post-conditions - const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); - expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress); - const newOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); - expect(newOwnerTakerAsset).to.be.bignumber.equal(makerAddress); - }); - it('should throw when maker does not own the token with id makerAssetId', async () => { // Construct Exchange parameters const makerAssetId = erc721TakerAssetIds[0]; @@ -839,86 +485,6 @@ describe('Exchange core', () => { exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), ); }); - - it('should successfully fill order when makerAsset is ERC721 and takerAsset is ERC20', async () => { - // Construct Exchange parameters - const makerAssetId = erc721MakerAssetIds[0]; - signedOrder = orderFactory.newSignedOrder({ - makerAssetAmount: new BigNumber(1), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - makerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), - takerAssetData: assetProxyUtils.encodeERC20AssetData(defaultTakerAssetAddress), - }); - // Verify pre-conditions - const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); - expect(initialOwnerMakerAsset).to.be.bignumber.equal(makerAddress); - // Call Exchange - erc20Balances = await erc20Wrapper.getBalancesAsync(); - const takerAssetFillAmount = signedOrder.takerAssetAmount; - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); - // Verify ERC721 token was transferred from Maker to Taker - const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); - expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress); - // Verify ERC20 tokens were transferred from Taker to Maker & fees were paid correctly - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), - ); - expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(signedOrder.makerFee), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(signedOrder.takerFee), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add( - signedOrder.makerFee.add(signedOrder.takerFee), - ), - ); - }); - - it('should successfully fill order when makerAsset is ERC20 and takerAsset is ERC721', async () => { - // Construct Exchange parameters - const takerAssetId = erc721TakerAssetIds[0]; - signedOrder = orderFactory.newSignedOrder({ - takerAssetAmount: new BigNumber(1), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - takerAssetData: assetProxyUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), - makerAssetData: assetProxyUtils.encodeERC20AssetData(defaultMakerAssetAddress), - }); - // Verify pre-conditions - const initialOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); - expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); - // Call Exchange - erc20Balances = await erc20Wrapper.getBalancesAsync(); - const takerAssetFillAmount = signedOrder.takerAssetAmount; - await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }); - // Verify ERC721 token was transferred from Taker to Maker - const newOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); - expect(newOwnerTakerAsset).to.be.bignumber.equal(makerAddress); - // Verify ERC20 tokens were transferred from Maker to Taker & fees were paid correctly - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerAssetAddress].add(signedOrder.makerAssetAmount), - ); - expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerAssetAddress].minus(signedOrder.makerAssetAmount), - ); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(signedOrder.makerFee), - ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].minus(signedOrder.takerFee), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add( - signedOrder.makerFee.add(signedOrder.takerFee), - ), - ); - }); }); }); // tslint:disable:max-file-line-count diff --git a/packages/contracts/test/exchange/fill_order.ts b/packages/contracts/test/exchange/fill_order.ts new file mode 100644 index 000000000..9c289b950 --- /dev/null +++ b/packages/contracts/test/exchange/fill_order.ts @@ -0,0 +1,186 @@ +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import * as _ from 'lodash'; + +import { chaiSetup } from '../../src/utils/chai_setup'; +import { CoreCombinatorialUtils, coreCombinatorialUtilsFactoryAsync } from '../../src/utils/core_combinatorial_utils'; +import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; + +import { + AssetDataScenario, + ExpirationTimeSecondsScenario, + FeeRecipientAddressScenario, + OrderAmountScenario, + OrderScenario, + TakerAssetFillAmountScenario, + TakerScenario, +} from '../../src/utils/types'; + +chaiSetup.configure(); +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +const defaultOrderScenario = { + takerScenario: TakerScenario.Unspecified, + feeRecipientScenario: FeeRecipientAddressScenario.EthUserAddress, + makerAssetAmountScenario: OrderAmountScenario.Large, + takerAssetAmountScenario: OrderAmountScenario.Large, + makerFeeScenario: OrderAmountScenario.Large, + takerFeeScenario: OrderAmountScenario.Large, + expirationTimeSecondsScenario: ExpirationTimeSecondsScenario.InFuture, + makerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, + takerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, +}; + +describe.only('FillOrder Tests', () => { + let coreCombinatorialUtils: CoreCombinatorialUtils; + + before(async () => { + await blockchainLifecycle.startAsync(); + coreCombinatorialUtils = await coreCombinatorialUtilsFactoryAsync(web3Wrapper, txDefaults); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('fillOrder', () => { + it('should transfer the correct amounts when makerAssetAmount === takerAssetAmount', async () => { + const fillScenario = { + orderScenario: defaultOrderScenario, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + it('should transfer the correct amounts when makerAssetAmount > takerAssetAmount', async () => { + const fillScenario = { + orderScenario: { + ...defaultOrderScenario, + takerAssetAmountScenario: OrderAmountScenario.Small, + }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + it('should transfer the correct amounts when makerAssetAmount < takerAssetAmount', async () => { + const fillScenario = { + orderScenario: { + ...defaultOrderScenario, + makerAssetAmountScenario: OrderAmountScenario.Small, + }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + it('should transfer the correct amounts when taker is specified and order is claimed by taker', async () => { + const fillScenario = { + orderScenario: { + ...defaultOrderScenario, + takerScenario: TakerScenario.CorrectlySpecified, + }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + it('should fill remaining value if takerAssetFillAmount > remaining takerAssetAmount', async () => { + const fillScenario = { + orderScenario: defaultOrderScenario, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + it('should throw when taker is specified and order is claimed by other', async () => { + const fillScenario = { + orderScenario: { + ...defaultOrderScenario, + takerScenario: TakerScenario.IncorrectlySpecified, + }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + + it('should throw if makerAssetAmount is 0', async () => { + const fillScenario = { + orderScenario: { + ...defaultOrderScenario, + makerAssetAmountScenario: OrderAmountScenario.Zero, + }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + + it('should throw if takerAssetAmount is 0', async () => { + const fillScenario = { + orderScenario: { + ...defaultOrderScenario, + takerAssetAmountScenario: OrderAmountScenario.Zero, + }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + + it('should throw if takerAssetFillAmount is 0', async () => { + const fillScenario = { + orderScenario: { + ...defaultOrderScenario, + }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.Zero, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + + it('should throw if an order is expired', async () => { + const fillScenario = { + orderScenario: { + ...defaultOrderScenario, + expirationTimeSecondsScenario: ExpirationTimeSecondsScenario.InPast, + }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + }); + + describe('Testing Exchange of ERC721 Tokens', () => { + it('should successfully exchange a single token between the maker and taker (via fillOrder)', async () => { + const fillScenario = { + orderScenario: { + ...defaultOrderScenario, + makerAssetDataScenario: AssetDataScenario.ERC721, + takerAssetDataScenario: AssetDataScenario.ERC721, + }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + + it('should successfully fill order when makerAsset is ERC721 and takerAsset is ERC20', async () => { + const fillScenario = { + orderScenario: { + ...defaultOrderScenario, + makerAssetDataScenario: AssetDataScenario.ERC721, + takerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, + }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + + it('should successfully fill order when makerAsset is ERC20 and takerAsset is ERC721', async () => { + const fillScenario = { + orderScenario: { + ...defaultOrderScenario, + makerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, + takerAssetDataScenario: AssetDataScenario.ERC721, + }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + }); +}); -- cgit From 432ff58107704bb4b2fde23f4a99c75f2bca24fa Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 14 Jun 2018 11:16:32 +0200 Subject: Fix calculation of expFilledTakerAmount and expected values in events emitted --- packages/contracts/src/utils/core_combinatorial_utils.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index dac4e9edd..4ce23d450 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -352,11 +352,14 @@ export class CoreCombinatorialUtils { const expZRXAssetBalanceOfFeeRecipient = await lazyStore.getBalanceAsync(this.zrxAssetData, feeRecipient); const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - const initialFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash); - const expFilledTakerAmount = initialFilledTakerAmount.add(takerAssetFillAmount); + const alreadyFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash); + const remainingTakerAmountToFill = signedOrder.takerAssetAmount.minus(alreadyFilledTakerAmount); + const expFilledTakerAmount = takerAssetFillAmount.gt(remainingTakerAmountToFill) + ? remainingTakerAmountToFill + : alreadyFilledTakerAmount.add(takerAssetFillAmount); const expFilledMakerAmount = orderUtils.getPartialAmount( - takerAssetFillAmount, + expFilledTakerAmount, signedOrder.takerAssetAmount, signedOrder.makerAssetAmount, ); @@ -380,7 +383,7 @@ export class CoreCombinatorialUtils { 'log.args.makerAssetFilledAmount', ); expect(log.args.takerAssetFilledAmount).to.be.bignumber.equal( - takerAssetFillAmount, + expFilledTakerAmount, 'log.args.takerAssetFilledAmount', ); const expMakerFeePaid = orderUtils.getPartialAmount( -- cgit From 57f37939d5d422d2bac90e7576030ff41b8de7b4 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 14 Jun 2018 11:16:51 +0200 Subject: Fix incorrect string values in enum --- packages/contracts/src/utils/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 273e8c2d6..318ea91f9 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -185,8 +185,8 @@ export enum AssetDataScenario { export enum TakerAssetFillAmountScenario { Zero = 'ZERO', GreaterThanRemainingFillableTakerAssetAmount = 'GREATER_THAN_REMAINING_FILLABLE_TAKER_ASSET_AMOUNT', - LessThanRemainingFillableTakerAssetAmount = 'GREATER_THAN_REMAINING_FILLABLE_TAKER_ASSET_AMOUNT', - ExactlyRemainingFillableTakerAssetAmount = 'GREATER_THAN_REMAINING_FILLABLE_TAKER_ASSET_AMOUNT', + LessThanRemainingFillableTakerAssetAmount = 'LESS_THAN_REMAINING_FILLABLE_TAKER_ASSET_AMOUNT', + ExactlyRemainingFillableTakerAssetAmount = 'EXACTLY_REMAINING_FILLABLE_TAKER_ASSET_AMOUNT', } export interface OrderScenario { -- cgit From c232a32991725f2f05d61c40f1fb899d138544d2 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 14 Jun 2018 11:17:13 +0200 Subject: Use a defaultFillScenario in fillOrder tests --- packages/contracts/test/exchange/fill_order.ts | 75 +++++++++++++------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/packages/contracts/test/exchange/fill_order.ts b/packages/contracts/test/exchange/fill_order.ts index 9c289b950..3f49f6119 100644 --- a/packages/contracts/test/exchange/fill_order.ts +++ b/packages/contracts/test/exchange/fill_order.ts @@ -18,16 +18,19 @@ import { chaiSetup.configure(); const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -const defaultOrderScenario = { - takerScenario: TakerScenario.Unspecified, - feeRecipientScenario: FeeRecipientAddressScenario.EthUserAddress, - makerAssetAmountScenario: OrderAmountScenario.Large, - takerAssetAmountScenario: OrderAmountScenario.Large, - makerFeeScenario: OrderAmountScenario.Large, - takerFeeScenario: OrderAmountScenario.Large, - expirationTimeSecondsScenario: ExpirationTimeSecondsScenario.InFuture, - makerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, - takerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, +const defaultFillScenario = { + orderScenario: { + takerScenario: TakerScenario.Unspecified, + feeRecipientScenario: FeeRecipientAddressScenario.EthUserAddress, + makerAssetAmountScenario: OrderAmountScenario.Large, + takerAssetAmountScenario: OrderAmountScenario.Large, + makerFeeScenario: OrderAmountScenario.Large, + takerFeeScenario: OrderAmountScenario.Large, + expirationTimeSecondsScenario: ExpirationTimeSecondsScenario.InFuture, + makerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, + takerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, + }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, }; describe.only('FillOrder Tests', () => { @@ -49,86 +52,83 @@ describe.only('FillOrder Tests', () => { describe('fillOrder', () => { it('should transfer the correct amounts when makerAssetAmount === takerAssetAmount', async () => { const fillScenario = { - orderScenario: defaultOrderScenario, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + ...defaultFillScenario, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); it('should transfer the correct amounts when makerAssetAmount > takerAssetAmount', async () => { const fillScenario = { + ...defaultFillScenario, orderScenario: { - ...defaultOrderScenario, + ...defaultFillScenario.orderScenario, takerAssetAmountScenario: OrderAmountScenario.Small, }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); it('should transfer the correct amounts when makerAssetAmount < takerAssetAmount', async () => { const fillScenario = { + ...defaultFillScenario, orderScenario: { - ...defaultOrderScenario, + ...defaultFillScenario.orderScenario, makerAssetAmountScenario: OrderAmountScenario.Small, }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); it('should transfer the correct amounts when taker is specified and order is claimed by taker', async () => { const fillScenario = { + ...defaultFillScenario, orderScenario: { - ...defaultOrderScenario, + ...defaultFillScenario.orderScenario, takerScenario: TakerScenario.CorrectlySpecified, }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); it('should fill remaining value if takerAssetFillAmount > remaining takerAssetAmount', async () => { const fillScenario = { - orderScenario: defaultOrderScenario, + ...defaultFillScenario, takerAssetFillAmountScenario: TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); it('should throw when taker is specified and order is claimed by other', async () => { const fillScenario = { + ...defaultFillScenario, orderScenario: { - ...defaultOrderScenario, + ...defaultFillScenario.orderScenario, takerScenario: TakerScenario.IncorrectlySpecified, }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); it('should throw if makerAssetAmount is 0', async () => { const fillScenario = { + ...defaultFillScenario, orderScenario: { - ...defaultOrderScenario, + ...defaultFillScenario.orderScenario, makerAssetAmountScenario: OrderAmountScenario.Zero, }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); it('should throw if takerAssetAmount is 0', async () => { const fillScenario = { + ...defaultFillScenario, orderScenario: { - ...defaultOrderScenario, + ...defaultFillScenario.orderScenario, takerAssetAmountScenario: OrderAmountScenario.Zero, }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); it('should throw if takerAssetFillAmount is 0', async () => { const fillScenario = { - orderScenario: { - ...defaultOrderScenario, - }, + ...defaultFillScenario, takerAssetFillAmountScenario: TakerAssetFillAmountScenario.Zero, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); @@ -136,11 +136,11 @@ describe.only('FillOrder Tests', () => { it('should throw if an order is expired', async () => { const fillScenario = { + ...defaultFillScenario, orderScenario: { - ...defaultOrderScenario, + ...defaultFillScenario.orderScenario, expirationTimeSecondsScenario: ExpirationTimeSecondsScenario.InPast, }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); @@ -149,36 +149,39 @@ describe.only('FillOrder Tests', () => { describe('Testing Exchange of ERC721 Tokens', () => { it('should successfully exchange a single token between the maker and taker (via fillOrder)', async () => { const fillScenario = { + ...defaultFillScenario, orderScenario: { - ...defaultOrderScenario, + ...defaultFillScenario.orderScenario, makerAssetDataScenario: AssetDataScenario.ERC721, takerAssetDataScenario: AssetDataScenario.ERC721, }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); it('should successfully fill order when makerAsset is ERC721 and takerAsset is ERC20', async () => { const fillScenario = { + ...defaultFillScenario, orderScenario: { - ...defaultOrderScenario, + ...defaultFillScenario.orderScenario, makerAssetDataScenario: AssetDataScenario.ERC721, takerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); it('should successfully fill order when makerAsset is ERC20 and takerAsset is ERC721', async () => { const fillScenario = { + ...defaultFillScenario, orderScenario: { - ...defaultOrderScenario, + ...defaultFillScenario.orderScenario, makerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, takerAssetDataScenario: AssetDataScenario.ERC721, }, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); -- cgit From 12033abe095e902102f1d56e7a2711a630e6826b Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 14 Jun 2018 12:07:57 +0200 Subject: Rename OrderAmountScenario to OrderAssetAmountScenario and convert generateOrderCombinations to generateFillOrderCombinations --- .../src/utils/core_combinatorial_utils.ts | 47 ++++++++++++---------- packages/contracts/src/utils/new_order_factory.ts | 34 ++++++++-------- packages/contracts/src/utils/types.ts | 10 ++--- .../contracts/test/exchange/combinatorial_tests.ts | 8 +--- packages/contracts/test/exchange/fill_order.ts | 20 ++++----- 5 files changed, 59 insertions(+), 60 deletions(-) diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index 4ce23d450..776d7b2fe 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -33,11 +33,11 @@ import { AssetDataScenario, ExpirationTimeSecondsScenario, FeeRecipientAddressScenario, - OrderAmountScenario, + FillScenario, + OrderAssetAmountScenario, OrderScenario, TakerAssetFillAmountScenario, TakerScenario, - FillScenario, } from '../utils/types'; chaiSetup.configure(); @@ -144,13 +144,13 @@ export class CoreCombinatorialUtils { public zrxAssetData: string; public exchangeWrapper: ExchangeWrapper; public assetWrapper: AssetWrapper; - public static generateOrderCombinations(): OrderScenario[] { + public static generateFillOrderCombinations(): FillScenario[] { const takerScenarios = [TakerScenario.Unspecified]; const feeRecipientScenarios = [FeeRecipientAddressScenario.EthUserAddress]; - const makerAssetAmountScenario = [OrderAmountScenario.Large]; - const takerAssetAmountScenario = [OrderAmountScenario.Large]; - const makerFeeScenario = [OrderAmountScenario.Large]; - const takerFeeScenario = [OrderAmountScenario.Large]; + const makerAssetAmountScenario = [OrderAssetAmountScenario.Large]; + const takerAssetAmountScenario = [OrderAssetAmountScenario.Large]; + const makerFeeScenario = [OrderAssetAmountScenario.Large]; + const takerFeeScenario = [OrderAssetAmountScenario.Large]; const expirationTimeSecondsScenario = [ExpirationTimeSecondsScenario.InFuture]; const makerAssetDataScenario = [ AssetDataScenario.ERC20FiveDecimals, @@ -164,7 +164,8 @@ export class CoreCombinatorialUtils { AssetDataScenario.ERC721, AssetDataScenario.ZRXFeeToken, ]; - const orderScenarioArrays = CoreCombinatorialUtils._allPossibleCases([ + const takerAssetFillAmountScenario = [TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount]; + const fillScenarioArrays = CoreCombinatorialUtils._allPossibleCases([ takerScenarios, feeRecipientScenarios, makerAssetAmountScenario, @@ -174,24 +175,28 @@ export class CoreCombinatorialUtils { expirationTimeSecondsScenario, makerAssetDataScenario, takerAssetDataScenario, + takerAssetFillAmountScenario, ]); - const orderScenarios = _.map(orderScenarioArrays, orderScenarioArray => { - const orderScenario: OrderScenario = { - takerScenario: orderScenarioArray[0] as TakerScenario, - feeRecipientScenario: orderScenarioArray[1] as FeeRecipientAddressScenario, - makerAssetAmountScenario: orderScenarioArray[2] as OrderAmountScenario, - takerAssetAmountScenario: orderScenarioArray[3] as OrderAmountScenario, - makerFeeScenario: orderScenarioArray[4] as OrderAmountScenario, - takerFeeScenario: orderScenarioArray[5] as OrderAmountScenario, - expirationTimeSecondsScenario: orderScenarioArray[6] as ExpirationTimeSecondsScenario, - makerAssetDataScenario: orderScenarioArray[7] as AssetDataScenario, - takerAssetDataScenario: orderScenarioArray[8] as AssetDataScenario, + const fillScenarios = _.map(fillScenarioArrays, fillScenarioArray => { + const fillScenario: FillScenario = { + orderScenario: { + takerScenario: fillScenarioArray[0] as TakerScenario, + feeRecipientScenario: fillScenarioArray[1] as FeeRecipientAddressScenario, + makerAssetAmountScenario: fillScenarioArray[2] as OrderAssetAmountScenario, + takerAssetAmountScenario: fillScenarioArray[3] as OrderAssetAmountScenario, + makerFeeScenario: fillScenarioArray[4] as OrderAssetAmountScenario, + takerFeeScenario: fillScenarioArray[5] as OrderAssetAmountScenario, + expirationTimeSecondsScenario: fillScenarioArray[6] as ExpirationTimeSecondsScenario, + makerAssetDataScenario: fillScenarioArray[7] as AssetDataScenario, + takerAssetDataScenario: fillScenarioArray[8] as AssetDataScenario, + }, + takerAssetFillAmountScenario: fillScenarioArray[9] as TakerAssetFillAmountScenario, }; - return orderScenario; + return fillScenario; }); - return orderScenarios; + return fillScenarios; } private static _allPossibleCases(arrays: string[][]): string[][] { if (arrays.length === 1) { diff --git a/packages/contracts/src/utils/new_order_factory.ts b/packages/contracts/src/utils/new_order_factory.ts index 7de7be3bd..22705d3e7 100644 --- a/packages/contracts/src/utils/new_order_factory.ts +++ b/packages/contracts/src/utils/new_order_factory.ts @@ -10,7 +10,7 @@ import { ERC721TokenIdsByOwner, ExpirationTimeSecondsScenario, FeeRecipientAddressScenario, - OrderAmountScenario, + OrderAssetAmountScenario, OrderScenario, TakerScenario, } from './types'; @@ -118,7 +118,7 @@ export class NewOrderFactory { } switch (orderScenario.makerAssetAmountScenario) { - case OrderAmountScenario.Large: + case OrderAssetAmountScenario.Large: switch (orderScenario.makerAssetDataScenario) { case AssetDataScenario.ZRXFeeToken: case AssetDataScenario.ERC20NonZRXEighteenDecimals: @@ -134,7 +134,7 @@ export class NewOrderFactory { throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); } break; - case OrderAmountScenario.Small: + case OrderAssetAmountScenario.Small: switch (orderScenario.makerAssetDataScenario) { case AssetDataScenario.ZRXFeeToken: case AssetDataScenario.ERC20NonZRXEighteenDecimals: @@ -150,15 +150,15 @@ export class NewOrderFactory { throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); } break; - case OrderAmountScenario.Zero: + case OrderAssetAmountScenario.Zero: makerAssetAmount = new BigNumber(0); break; default: - throw errorUtils.spawnSwitchErr('OrderAmountScenario', orderScenario.makerAssetAmountScenario); + throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.makerAssetAmountScenario); } switch (orderScenario.takerAssetAmountScenario) { - case OrderAmountScenario.Large: + case OrderAssetAmountScenario.Large: switch (orderScenario.takerAssetDataScenario) { case AssetDataScenario.ERC20NonZRXEighteenDecimals: case AssetDataScenario.ZRXFeeToken: @@ -174,7 +174,7 @@ export class NewOrderFactory { throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); } break; - case OrderAmountScenario.Small: + case OrderAssetAmountScenario.Small: switch (orderScenario.takerAssetDataScenario) { case AssetDataScenario.ERC20NonZRXEighteenDecimals: case AssetDataScenario.ZRXFeeToken: @@ -190,39 +190,39 @@ export class NewOrderFactory { throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); } break; - case OrderAmountScenario.Zero: + case OrderAssetAmountScenario.Zero: takerAssetAmount = new BigNumber(0); break; default: - throw errorUtils.spawnSwitchErr('OrderAmountScenario', orderScenario.takerAssetAmountScenario); + throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.takerAssetAmountScenario); } switch (orderScenario.makerFeeScenario) { - case OrderAmountScenario.Large: + case OrderAssetAmountScenario.Large: makerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; break; - case OrderAmountScenario.Small: + case OrderAssetAmountScenario.Small: makerFee = POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS; break; - case OrderAmountScenario.Zero: + case OrderAssetAmountScenario.Zero: makerFee = new BigNumber(0); break; default: - throw errorUtils.spawnSwitchErr('OrderAmountScenario', orderScenario.makerFeeScenario); + throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.makerFeeScenario); } switch (orderScenario.takerFeeScenario) { - case OrderAmountScenario.Large: + case OrderAssetAmountScenario.Large: takerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; break; - case OrderAmountScenario.Small: + case OrderAssetAmountScenario.Small: takerFee = POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS; break; - case OrderAmountScenario.Zero: + case OrderAssetAmountScenario.Zero: takerFee = new BigNumber(0); break; default: - throw errorUtils.spawnSwitchErr('OrderAmountScenario', orderScenario.takerFeeScenario); + throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.takerFeeScenario); } switch (orderScenario.expirationTimeSecondsScenario) { diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 318ea91f9..f0e2dde02 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -158,7 +158,7 @@ export enum FeeRecipientAddressScenario { EthUserAddress = 'ETH_USER_ADDRESS', } -export enum OrderAmountScenario { +export enum OrderAssetAmountScenario { Zero = 'ZERO', Large = 'LARGE', Small = 'SMALL', @@ -192,10 +192,10 @@ export enum TakerAssetFillAmountScenario { export interface OrderScenario { takerScenario: TakerScenario; feeRecipientScenario: FeeRecipientAddressScenario; - makerAssetAmountScenario: OrderAmountScenario; - takerAssetAmountScenario: OrderAmountScenario; - makerFeeScenario: OrderAmountScenario; - takerFeeScenario: OrderAmountScenario; + makerAssetAmountScenario: OrderAssetAmountScenario; + takerAssetAmountScenario: OrderAssetAmountScenario; + makerFeeScenario: OrderAssetAmountScenario; + takerFeeScenario: OrderAssetAmountScenario; expirationTimeSecondsScenario: ExpirationTimeSecondsScenario; makerAssetDataScenario: AssetDataScenario; takerAssetDataScenario: AssetDataScenario; diff --git a/packages/contracts/test/exchange/combinatorial_tests.ts b/packages/contracts/test/exchange/combinatorial_tests.ts index fd9193d69..754af9b08 100644 --- a/packages/contracts/test/exchange/combinatorial_tests.ts +++ b/packages/contracts/test/exchange/combinatorial_tests.ts @@ -42,13 +42,7 @@ describe('Combinatorial tests', () => { }); }; - const allOrderScenarios = CoreCombinatorialUtils.generateOrderCombinations(); - const allFillScenarios = _.map(allOrderScenarios, orderScenario => { - return { - orderScenario, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, - }; - }); + const allFillScenarios = CoreCombinatorialUtils.generateFillOrderCombinations(); describe('Fills orders', () => test(allFillScenarios)); }); diff --git a/packages/contracts/test/exchange/fill_order.ts b/packages/contracts/test/exchange/fill_order.ts index 3f49f6119..2498ee27e 100644 --- a/packages/contracts/test/exchange/fill_order.ts +++ b/packages/contracts/test/exchange/fill_order.ts @@ -9,7 +9,7 @@ import { AssetDataScenario, ExpirationTimeSecondsScenario, FeeRecipientAddressScenario, - OrderAmountScenario, + OrderAssetAmountScenario, OrderScenario, TakerAssetFillAmountScenario, TakerScenario, @@ -22,10 +22,10 @@ const defaultFillScenario = { orderScenario: { takerScenario: TakerScenario.Unspecified, feeRecipientScenario: FeeRecipientAddressScenario.EthUserAddress, - makerAssetAmountScenario: OrderAmountScenario.Large, - takerAssetAmountScenario: OrderAmountScenario.Large, - makerFeeScenario: OrderAmountScenario.Large, - takerFeeScenario: OrderAmountScenario.Large, + makerAssetAmountScenario: OrderAssetAmountScenario.Large, + takerAssetAmountScenario: OrderAssetAmountScenario.Large, + makerFeeScenario: OrderAssetAmountScenario.Large, + takerFeeScenario: OrderAssetAmountScenario.Large, expirationTimeSecondsScenario: ExpirationTimeSecondsScenario.InFuture, makerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, takerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, @@ -33,7 +33,7 @@ const defaultFillScenario = { takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, }; -describe.only('FillOrder Tests', () => { +describe('FillOrder Tests', () => { let coreCombinatorialUtils: CoreCombinatorialUtils; before(async () => { @@ -61,7 +61,7 @@ describe.only('FillOrder Tests', () => { ...defaultFillScenario, orderScenario: { ...defaultFillScenario.orderScenario, - takerAssetAmountScenario: OrderAmountScenario.Small, + takerAssetAmountScenario: OrderAssetAmountScenario.Small, }, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); @@ -71,7 +71,7 @@ describe.only('FillOrder Tests', () => { ...defaultFillScenario, orderScenario: { ...defaultFillScenario.orderScenario, - makerAssetAmountScenario: OrderAmountScenario.Small, + makerAssetAmountScenario: OrderAssetAmountScenario.Small, }, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); @@ -109,7 +109,7 @@ describe.only('FillOrder Tests', () => { ...defaultFillScenario, orderScenario: { ...defaultFillScenario.orderScenario, - makerAssetAmountScenario: OrderAmountScenario.Zero, + makerAssetAmountScenario: OrderAssetAmountScenario.Zero, }, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); @@ -120,7 +120,7 @@ describe.only('FillOrder Tests', () => { ...defaultFillScenario, orderScenario: { ...defaultFillScenario.orderScenario, - takerAssetAmountScenario: OrderAmountScenario.Zero, + takerAssetAmountScenario: OrderAssetAmountScenario.Zero, }, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); -- cgit From eea86757d5cdd63de4b0b06f7dd76fc2c2ddbd36 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 15 Jun 2018 00:00:02 +0200 Subject: - Refactor assetWrapper to contain more of the normalizing logic instead of erc20Wrapper and erc721Wrapper - Add burn method to DummyERC721Token - Add additional methods to assetWrapper to set balance/allowances on ERC20 and ERC721 tokens - Use approve instead of approveAll for ERC721 tokens --- .../src/abstract/abstract_asset_wrapper.ts | 5 - .../test/DummyERC721Token/DummyERC721Token.sol | 16 ++ packages/contracts/src/utils/asset_wrapper.ts | 185 ++++++++++++++++++++- packages/contracts/src/utils/constants.ts | 2 + packages/contracts/src/utils/erc20_wrapper.ts | 48 +++--- packages/contracts/src/utils/erc721_wrapper.ts | 127 +++++++++----- packages/contracts/test/asset_proxy/proxies.ts | 3 +- packages/contracts/test/exchange/wrapper.ts | 2 +- .../artifacts/2.0.0/DummyERC721Token.json | 34 +++- .../order-utils/src/exchange_transfer_simulator.ts | 3 +- 10 files changed, 341 insertions(+), 84 deletions(-) diff --git a/packages/contracts/src/abstract/abstract_asset_wrapper.ts b/packages/contracts/src/abstract/abstract_asset_wrapper.ts index d7ab58fea..521335f18 100644 --- a/packages/contracts/src/abstract/abstract_asset_wrapper.ts +++ b/packages/contracts/src/abstract/abstract_asset_wrapper.ts @@ -2,9 +2,4 @@ import { BigNumber } from '@0xproject/utils'; export abstract class AbstractAssetWrapper { public abstract getProxyId(): number; - public abstract async setBalancesAndAllowancesAsync(): Promise; - public abstract async getBalanceAsync(owner: string, assetData: string): Promise; - public abstract async getProxyAllowanceAsync(owner: string, assetData: string): Promise; - public abstract getTokenOwnerAddresses(): string[]; - public abstract getTokenAddresses(): string[]; } diff --git a/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol b/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol index 5503eb2f2..78ea96447 100644 --- a/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol +++ b/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol @@ -56,4 +56,20 @@ contract DummyERC721Token is ); _mint(to, tokenId); } + + /** + * @dev Function to burn a token + * @dev Reverts if the given token ID doesn't exist + * @param tokenId uint256 ID of the token to be minted by the msg.sender + */ + function burn(address owner, uint256 tokenId) + public + onlyOwner + { + require( + exists(tokenId), + "Token with tokenId does not exist." + ); + _burn(owner, tokenId); + } } diff --git a/packages/contracts/src/utils/asset_wrapper.ts b/packages/contracts/src/utils/asset_wrapper.ts index 4c345aa30..462a5086a 100644 --- a/packages/contracts/src/utils/asset_wrapper.ts +++ b/packages/contracts/src/utils/asset_wrapper.ts @@ -1,9 +1,13 @@ import { assetProxyUtils } from '@0xproject/order-utils'; -import { BigNumber } from '@0xproject/utils'; +import { BigNumber, errorUtils } from '@0xproject/utils'; import * as _ from 'lodash'; import { AbstractAssetWrapper } from '../abstract/abstract_asset_wrapper'; +import { constants } from './constants'; +import { ERC20Wrapper } from './erc20_wrapper'; +import { ERC721Wrapper } from './erc721_wrapper'; + interface ProxyIdToAssetWrappers { [proxyId: number]: AbstractAssetWrapper; } @@ -17,16 +21,179 @@ export class AssetWrapper { this._proxyIdToAssetWrappers[proxyId] = assetWrapper; }); } - public async getBalanceAsync(owner: string, assetData: string): Promise { + public async getBalanceAsync(userAddress: string, assetData: string): Promise { + const proxyId = assetProxyUtils.decodeAssetDataId(assetData); + switch (proxyId) { + case constants.ERC20_PROXY_ID: { + const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; + const balance = await assetWrapper.getBalanceAsync(userAddress, assetData); + return balance; + } + + case constants.ERC721_PROXY_ID: { + const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper; + const assetProxyData = assetProxyUtils.decodeERC721AssetData(assetData); + const isOwner = await assetWrapper.isOwnerAsync( + userAddress, + assetProxyData.tokenAddress, + assetProxyData.tokenId, + ); + const balance = isOwner ? new BigNumber(1) : new BigNumber(0); + return balance; + } + + default: + throw errorUtils.spawnSwitchErr('proxyId', proxyId); + } + } + public async setBalanceAsync(userAddress: string, assetData: string, desiredBalance: BigNumber): Promise { + const proxyId = assetProxyUtils.decodeAssetDataId(assetData); + switch (proxyId) { + case constants.ERC20_PROXY_ID: { + const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; + await assetWrapper.setBalanceAsync(userAddress, assetData, desiredBalance); + return; + } + + case constants.ERC721_PROXY_ID: { + if (!desiredBalance.eq(0) && !desiredBalance.eq(1)) { + throw new Error(`Balance for ERC721 token can only be set to 0 or 1. Got: ${desiredBalance}`); + } + const erc721Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper; + const assetProxyData = assetProxyUtils.decodeERC721AssetData(assetData); + const doesTokenExist = erc721Wrapper.doesTokenExistAsync( + assetProxyData.tokenAddress, + assetProxyData.tokenId, + ); + if (!doesTokenExist && desiredBalance.eq(1)) { + await erc721Wrapper.mintAsync(assetProxyData.tokenAddress, assetProxyData.tokenId, userAddress); + return; + } else if (!doesTokenExist && desiredBalance.eq(0)) { + return; // noop + } + const tokenOwner = await erc721Wrapper.ownerOfAsync( + assetProxyData.tokenAddress, + assetProxyData.tokenId, + ); + if (userAddress !== tokenOwner && desiredBalance.eq(1)) { + await erc721Wrapper.transferFromAsync( + assetProxyData.tokenAddress, + assetProxyData.tokenId, + tokenOwner, + userAddress, + ); + } else if ( + (userAddress !== tokenOwner && desiredBalance.eq(0)) || + (tokenOwner === userAddress && desiredBalance.eq(1)) + ) { + return; // noop + } else if (tokenOwner === userAddress && desiredBalance.eq(0)) { + // Burn token + await erc721Wrapper.burnAsync(assetProxyData.tokenAddress, assetProxyData.tokenId, userAddress); + return; + } + break; + } + + default: + throw errorUtils.spawnSwitchErr('proxyId', proxyId); + } + } + public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise { const proxyId = assetProxyUtils.decodeAssetDataId(assetData); - const assetWrapper = this._proxyIdToAssetWrappers[proxyId]; - const balance = await assetWrapper.getBalanceAsync(owner, assetData); - return balance; + switch (proxyId) { + case constants.ERC20_PROXY_ID: { + const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; + const allowance = await assetWrapper.getProxyAllowanceAsync(userAddress, assetData); + return allowance; + } + + case constants.ERC721_PROXY_ID: { + const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper; + const erc721ProxyData = assetProxyUtils.decodeERC721AssetData(assetData); + const isProxyApproved = await assetWrapper.isProxyApprovedAsync( + erc721ProxyData.tokenAddress, + erc721ProxyData.tokenId, + ); + const isProxyApprovedForAllAsync = await assetWrapper.isProxyApprovedForAllAsync( + userAddress, + erc721ProxyData.tokenAddress, + erc721ProxyData.tokenId, + ); + const allowance = isProxyApproved || isProxyApprovedForAllAsync ? new BigNumber(1) : new BigNumber(0); + return allowance; + } + + default: + throw errorUtils.spawnSwitchErr('proxyId', proxyId); + } } - public async getProxyAllowanceAsync(owner: string, assetData: string): Promise { + public async setProxyAllowanceAsync( + userAddress: string, + assetData: string, + desiredAllowance: BigNumber, + ): Promise { const proxyId = assetProxyUtils.decodeAssetDataId(assetData); - const assetWrapper = this._proxyIdToAssetWrappers[proxyId]; - const balance = await assetWrapper.getProxyAllowanceAsync(owner, assetData); - return balance; + switch (proxyId) { + case constants.ERC20_PROXY_ID: { + const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; + await assetWrapper.setAllowanceAsync(userAddress, assetData, desiredAllowance); + return; + } + + case constants.ERC721_PROXY_ID: { + if (!desiredAllowance.eq(0) && !desiredAllowance.eq(1)) { + throw new Error(`Allowance for ERC721 token can only be set to 0 or 1. Got: ${desiredAllowance}`); + } + const erc721Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper; + const assetProxyData = assetProxyUtils.decodeERC721AssetData(assetData); + + const doesTokenExist = await erc721Wrapper.doesTokenExistAsync( + assetProxyData.tokenAddress, + assetProxyData.tokenId, + ); + if (!doesTokenExist) { + throw new Error( + `Cannot setProxyAllowance on non-existent token: ${assetProxyData.tokenAddress} ${ + assetProxyData.tokenId + }`, + ); + } + const isProxyApprovedForAll = await erc721Wrapper.isProxyApprovedForAllAsync( + userAddress, + assetProxyData.tokenAddress, + assetProxyData.tokenId, + ); + // TODO: We should have a way to deal with this. Things get hairier once we are testing + // batch fills + if (isProxyApprovedForAll) { + throw new Error(`We don't currently support the use of "approveAll" functionality for ERC721.`); + } + + const isProxyApproved = await erc721Wrapper.isProxyApprovedAsync( + assetProxyData.tokenAddress, + assetProxyData.tokenId, + ); + if (!isProxyApproved && desiredAllowance.eq(1)) { + await erc721Wrapper.approveProxyAsync(assetProxyData.tokenAddress, assetProxyData.tokenId); + } else if (isProxyApproved && desiredAllowance.eq(0)) { + await erc721Wrapper.approveAsync( + constants.NULL_ADDRESS, + assetProxyData.tokenAddress, + assetProxyData.tokenId, + ); + } else if ( + (!isProxyApproved && desiredAllowance.eq(0)) || + (isProxyApproved && desiredAllowance.eq(1)) + ) { + return; // noop + } + + break; + } + + default: + throw errorUtils.spawnSwitchErr('proxyId', proxyId); + } } } diff --git a/packages/contracts/src/utils/constants.ts b/packages/contracts/src/utils/constants.ts index ec3c8fd36..c8b684c41 100644 --- a/packages/contracts/src/utils/constants.ts +++ b/packages/contracts/src/utils/constants.ts @@ -27,6 +27,8 @@ export const constants = { LIB_BYTES_GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED: 'GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED', ERC20_INSUFFICIENT_BALANCE: 'Insufficient balance to complete transfer.', ERC20_INSUFFICIENT_ALLOWANCE: 'Insufficient allowance to complete transfer.', + ERC20_PROXY_ID: 1, + ERC721_PROXY_ID: 2, TESTRPC_NETWORK_ID: 50, // Note(albrow): In practice V8 and most other engines limit the minimum // interval for setInterval to 10ms. We still set it to 0 here in order to diff --git a/packages/contracts/src/utils/erc20_wrapper.ts b/packages/contracts/src/utils/erc20_wrapper.ts index 2d367ae1c..ca2ca492b 100644 --- a/packages/contracts/src/utils/erc20_wrapper.ts +++ b/packages/contracts/src/utils/erc20_wrapper.ts @@ -83,29 +83,30 @@ export class ERC20Wrapper { } } } - public async getBalanceAsync(owner: string, assetData: string): Promise { - const erc20ProxyData = assetProxyUtils.decodeERC20AssetData(assetData); - const tokenAddress = erc20ProxyData.tokenAddress; - const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); - if (_.isUndefined(tokenContractIfExists)) { - throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); - } - const balance = new BigNumber(await tokenContractIfExists.balanceOf.callAsync(owner)); + public async getBalanceAsync(userAddress: string, assetData: string): Promise { + const tokenContract = this._getTokenContractFromAssetData(assetData); + const balance = new BigNumber(await tokenContract.balanceOf.callAsync(userAddress)); return balance; } - public async getProxyAllowanceAsync(owner: string, assetData: string): Promise { - const erc20ProxyData = assetProxyUtils.decodeERC20AssetData(assetData); - const tokenAddress = erc20ProxyData.tokenAddress; - this._validateProxyContractExistsOrThrow(); - const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); - if (_.isUndefined(tokenContractIfExists)) { - throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); - } - const allowance = new BigNumber( - await tokenContractIfExists.allowance.callAsync(owner, (this._proxyContract as ERC20ProxyContract).address), - ); + public async setBalanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise { + const tokenContract = this._getTokenContractFromAssetData(assetData); + await tokenContract.setBalance.sendTransactionAsync(userAddress, amount, { + from: this._contractOwnerAddress, + }); + } + public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise { + const tokenContract = this._getTokenContractFromAssetData(assetData); + const proxyAddress = (this._proxyContract as ERC20ProxyContract).address; + const allowance = new BigNumber(await tokenContract.allowance.callAsync(userAddress, proxyAddress)); return allowance; } + public async setAllowanceAsync(userAddress: string, assetData: string, amount: BigNumber): Promise { + const tokenContract = this._getTokenContractFromAssetData(assetData); + const proxyAddress = (this._proxyContract as ERC20ProxyContract).address; + await tokenContract.approve.sendTransactionAsync(proxyAddress, amount, { + from: userAddress, + }); + } public async getBalancesAsync(): Promise { this._validateDummyTokenContractsExistOrThrow(); const balancesByOwner: ERC20BalancesByOwner = {}; @@ -138,6 +139,15 @@ export class ERC20Wrapper { const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address); return tokenAddresses; } + private _getTokenContractFromAssetData(assetData: string): DummyERC20TokenContract { + const erc20ProxyData = assetProxyUtils.decodeERC20AssetData(assetData); + const tokenAddress = erc20ProxyData.tokenAddress; + const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); + if (_.isUndefined(tokenContractIfExists)) { + throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); + } + return tokenContractIfExists; + } private _validateDummyTokenContractsExistOrThrow(): void { if (_.isUndefined(this._dummyTokenContracts)) { throw new Error('Dummy ERC20 tokens not yet deployed, please call "deployDummyTokensAsync"'); diff --git a/packages/contracts/src/utils/erc721_wrapper.ts b/packages/contracts/src/utils/erc721_wrapper.ts index 6a6f10fa7..7da3b6781 100644 --- a/packages/contracts/src/utils/erc721_wrapper.ts +++ b/packages/contracts/src/utils/erc721_wrapper.ts @@ -63,12 +63,7 @@ export class ERC721Wrapper { for (const tokenOwnerAddress of this._tokenOwnerAddresses) { for (let i = 0; i < constants.NUM_ERC721_TOKENS_TO_MINT; i++) { const tokenId = generatePseudoRandomSalt(); - await this._web3Wrapper.awaitTransactionSuccessAsync( - await dummyTokenContract.mint.sendTransactionAsync(tokenOwnerAddress, tokenId, { - from: this._contractOwnerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); + await this.mintAsync(dummyTokenContract.address, tokenId, tokenOwnerAddress); if (_.isUndefined(this._initialTokenIdsByOwner[tokenOwnerAddress])) { this._initialTokenIdsByOwner[tokenOwnerAddress] = { [dummyTokenContract.address]: [], @@ -78,45 +73,92 @@ export class ERC721Wrapper { this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address] = []; } this._initialTokenIdsByOwner[tokenOwnerAddress][dummyTokenContract.address].push(tokenId); + + await this.approveProxyAsync(dummyTokenContract.address, tokenId); } - const shouldApprove = true; - await this._web3Wrapper.awaitTransactionSuccessAsync( - await dummyTokenContract.setApprovalForAll.sendTransactionAsync( - (this._proxyContract as ERC721ProxyContract).address, - shouldApprove, - { from: tokenOwnerAddress }, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); } } } - public async getBalanceAsync(owner: string, assetData: string): Promise { - const erc721ProxyData = assetProxyUtils.decodeERC721AssetData(assetData); - const tokenAddress = erc721ProxyData.tokenAddress; - const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); - if (_.isUndefined(tokenContractIfExists)) { - throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); - } - const tokenId = erc721ProxyData.tokenId; - const tokenOwner = await tokenContractIfExists.ownerOf.callAsync(tokenId); - const balance = tokenOwner === owner ? new BigNumber(1) : new BigNumber(0); - return balance; - } - public async getProxyAllowanceAsync(owner: string, assetData: string): Promise { - const erc721ProxyData = assetProxyUtils.decodeERC721AssetData(assetData); - const tokenAddress = erc721ProxyData.tokenAddress; + public async doesTokenExistAsync(tokenAddress: string, tokenId: BigNumber): Promise { + const tokenContract = this._getTokenContractFromAssetData(tokenAddress); + const doesExist = await tokenContract.exists.callAsync(tokenId); + return doesExist; + } + public async approveProxyAsync(tokenAddress: string, tokenId: BigNumber): Promise { + const proxyAddress = (this._proxyContract as ERC721ProxyContract).address; + await this.approveAsync(proxyAddress, tokenAddress, tokenId); + } + public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise { + const tokenContract = this._getTokenContractFromAssetData(tokenAddress); + const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId); + await this._web3Wrapper.awaitTransactionSuccessAsync( + await tokenContract.approve.sendTransactionAsync(to, tokenId, { + from: tokenOwner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + } + public async transferFromAsync( + tokenAddress: string, + tokenId: BigNumber, + currentOwner: string, + userAddress: string, + ): Promise { + const tokenContract = this._getTokenContractFromAssetData(tokenAddress); + await this._web3Wrapper.awaitTransactionSuccessAsync( + await tokenContract.transferFrom.sendTransactionAsync(currentOwner, userAddress, tokenId, { + from: currentOwner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + } + public async mintAsync(tokenAddress: string, tokenId: BigNumber, userAddress: string): Promise { + const tokenContract = this._getTokenContractFromAssetData(tokenAddress); + await this._web3Wrapper.awaitTransactionSuccessAsync( + await tokenContract.mint.sendTransactionAsync(userAddress, tokenId, { + from: this._contractOwnerAddress, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + } + public async burnAsync(tokenAddress: string, tokenId: BigNumber, owner: string): Promise { + const tokenContract = this._getTokenContractFromAssetData(tokenAddress); + await this._web3Wrapper.awaitTransactionSuccessAsync( + await tokenContract.burn.sendTransactionAsync(owner, tokenId, { + from: this._contractOwnerAddress, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + } + public async ownerOfAsync(tokenAddress: string, tokenId: BigNumber): Promise { + const tokenContract = this._getTokenContractFromAssetData(tokenAddress); + const owner = await tokenContract.ownerOf.callAsync(tokenId); + return owner; + } + public async isOwnerAsync(userAddress: string, tokenAddress: string, tokenId: BigNumber): Promise { + const tokenContract = this._getTokenContractFromAssetData(tokenAddress); + const tokenOwner = await tokenContract.ownerOf.callAsync(tokenId); + const isOwner = tokenOwner === userAddress; + return isOwner; + } + public async isProxyApprovedForAllAsync( + userAddress: string, + tokenAddress: string, + tokenId: BigNumber, + ): Promise { this._validateProxyContractExistsOrThrow(); - const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); - if (_.isUndefined(tokenContractIfExists)) { - throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); - } + const tokenContract = this._getTokenContractFromAssetData(tokenAddress); const operator = (this._proxyContract as ERC721ProxyContract).address; - const didApproveAll = await tokenContractIfExists.isApprovedForAll.callAsync(owner, operator); - const tokenId = erc721ProxyData.tokenId; - const allowedAddress = await tokenContractIfExists.getApproved.callAsync(tokenId); - const allowance = allowedAddress === operator || didApproveAll ? new BigNumber(1) : new BigNumber(0); - return allowance; + const didApproveAll = await tokenContract.isApprovedForAll.callAsync(userAddress, operator); + return didApproveAll; + } + public async isProxyApprovedAsync(tokenAddress: string, tokenId: BigNumber): Promise { + this._validateProxyContractExistsOrThrow(); + const tokenContract = this._getTokenContractFromAssetData(tokenAddress); + const approvedAddress = await tokenContract.getApproved.callAsync(tokenId); + const proxyAddress = (this._proxyContract as ERC721ProxyContract).address; + const isProxyAnApprovedOperator = approvedAddress === proxyAddress; + return isProxyAnApprovedOperator; } public async getBalancesAsync(): Promise { this._validateDummyTokenContractsExistOrThrow(); @@ -160,6 +202,13 @@ export class ERC721Wrapper { const tokenAddresses = _.map(this._dummyTokenContracts, dummyTokenContract => dummyTokenContract.address); return tokenAddresses; } + private _getTokenContractFromAssetData(tokenAddress: string): DummyERC721TokenContract { + const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === tokenAddress); + if (_.isUndefined(tokenContractIfExists)) { + throw new Error(`Token: ${tokenAddress} was not deployed through ERC20Wrapper`); + } + return tokenContractIfExists; + } private _validateDummyTokenContractsExistOrThrow(): void { if (_.isUndefined(this._dummyTokenContracts)) { throw new Error('Dummy ERC721 tokens not yet deployed, please call "deployDummyTokensAsync"'); diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts index 9760d3b9c..4a51e0db8 100644 --- a/packages/contracts/test/asset_proxy/proxies.ts +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -473,7 +473,8 @@ describe('Asset Transfer Proxies', () => { txHash, constants.AWAIT_TRANSACTION_MINED_MS, ); - expect(res.logs.length).to.equal(numTransfers); + const numApproveEvents = 2; + expect(res.logs.length).to.equal(numTransfers + numApproveEvents); const newOwnerMakerAssetA = await erc721Token.ownerOf.callAsync(makerTokenIdA); const newOwnerMakerAssetB = await erc721Token.ownerOf.callAsync(makerTokenIdB); diff --git a/packages/contracts/test/exchange/wrapper.ts b/packages/contracts/test/exchange/wrapper.ts index abba1ac4f..00c5e4352 100644 --- a/packages/contracts/test/exchange/wrapper.ts +++ b/packages/contracts/test/exchange/wrapper.ts @@ -369,7 +369,7 @@ describe('Exchange wrappers', () => { // 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: 270000, + gas: 280000, }); // Verify post-conditions const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); diff --git a/packages/migrations/artifacts/2.0.0/DummyERC721Token.json b/packages/migrations/artifacts/2.0.0/DummyERC721Token.json index 9bdf7787a..84bc92299 100644 --- a/packages/migrations/artifacts/2.0.0/DummyERC721Token.json +++ b/packages/migrations/artifacts/2.0.0/DummyERC721Token.json @@ -201,6 +201,24 @@ "stateMutability": "view", "type": "function" }, + { + "constant": false, + "inputs": [ + { + "name": "owner", + "type": "address" + }, + { + "name": "tokenId", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, { "constant": false, "inputs": [ @@ -367,15 +385,15 @@ "evm": { "bytecode": { "linkReferences": {}, - "object": "0x60806040523480156200001157600080fd5b506040516200137638038062001376833981018060405262000037919081019062000184565b60008054600160a060020a031916331790558151829082906200006290600190602085019062000083565b5080516200007890600290602084019062000083565b505050505062000274565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620000c657805160ff1916838001178555620000f6565b82800160010185558215620000f6579182015b82811115620000f6578251825591602001919060010190620000d9565b506200010492915062000108565b5090565b6200012591905b808211156200010457600081556001016200010f565b90565b6000601f820183136200013a57600080fd5b8151620001516200014b8262000219565b620001f2565b915080825260208301602083018583830111156200016e57600080fd5b6200017b83828462000241565b50505092915050565b600080604083850312156200019857600080fd5b82516001604060020a03811115620001af57600080fd5b620001bd8582860162000128565b92505060208301516001604060020a03811115620001da57600080fd5b620001e88582860162000128565b9150509250929050565b6040518181016001604060020a03811182821017156200021157600080fd5b604052919050565b60006001604060020a038211156200023057600080fd5b506020601f91909101601f19160190565b60005b838110156200025e57818101518382015260200162000244565b838111156200026e576000848401525b50505050565b6110f280620002846000396000f3006080604052600436106100da5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde0381146100df578063081812fc1461010a578063095ea7b31461013757806323b872dd1461015957806340c10f191461017957806342842e0e146101995780634f558e79146101b95780636352211e146101e657806370a08231146102065780638da5cb5b1461023357806395d89b4114610248578063a22cb4651461025d578063b88d4fde1461027d578063e985e9c51461029d578063f2fde38b146102bd575b600080fd5b3480156100eb57600080fd5b506100f46102dd565b6040516101019190610fae565b60405180910390f35b34801561011657600080fd5b5061012a610125366004610e3c565b610372565b6040516101019190610f5c565b34801561014357600080fd5b50610157610152366004610dee565b61038d565b005b34801561016557600080fd5b50610157610174366004610cf8565b610483565b34801561018557600080fd5b50610157610194366004610dee565b610532565b3480156101a557600080fd5b506101576101b4366004610cf8565b6105cd565b3480156101c557600080fd5b506101d96101d4366004610e3c565b610605565b6040516101019190610fa0565b3480156101f257600080fd5b5061012a610201366004610e3c565b610622565b34801561021257600080fd5b50610226610221366004610ca0565b61064c565b6040516101019190610fdf565b34801561023f57600080fd5b5061012a61067f565b34801561025457600080fd5b506100f461068e565b34801561026957600080fd5b50610157610278366004610dbe565b6106ec565b34801561028957600080fd5b50610157610298366004610d45565b610771565b3480156102a957600080fd5b506101d96102b8366004610cbe565b6107b0565b3480156102c957600080fd5b506101576102d8366004610ca0565b6107de565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156103685780601f1061033d57610100808354040283529160200191610368565b820191906000526020600020905b81548152906001019060200180831161034b57829003601f168201915b5050505050905090565b600090815260046020526040902054600160a060020a031690565b600061039882610622565b9050600160a060020a0383811690821614156103b357600080fd5b33600160a060020a03821614806103cf57506103cf81336107b0565b15156103da57600080fd5b60006103e583610372565b600160a060020a03161415806104035750600160a060020a03831615155b1561047e5760008281526004602052604090819020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a038681169182179092559151908316907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610475908690610fdf565b60405180910390a35b505050565b8061048e338261085d565b151561049957600080fd5b600160a060020a03841615156104ae57600080fd5b600160a060020a03831615156104c357600080fd5b6104cd84836108bc565b6104d78483610962565b6104e183836109f2565b82600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516105249190610fdf565b60405180910390a350505050565b600054600160a060020a0316331461057f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161057690610fcf565b60405180910390fd5b61058881610605565b156105bf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161057690610fbf565b6105c98282610a83565b5050565b806105d8338261085d565b15156105e357600080fd5b6105ff8484846020604051908101604052806000815250610771565b50505050565b600090815260036020526040902054600160a060020a0316151590565b600081815260036020526040812054600160a060020a031680151561064657600080fd5b92915050565b6000600160a060020a038216151561066357600080fd5b50600160a060020a031660009081526005602052604090205490565b600054600160a060020a031681565b60028054604080516020601f60001961010060018716150201909416859004938401819004810282018101909252828152606093909290918301828280156103685780601f1061033d57610100808354040283529160200191610368565b600160a060020a03821633141561070257600080fd5b336000818152600660209081526040808320600160a060020a038716808552925291829020805460ff191685151517905590519091907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3190610765908590610fa0565b60405180910390a35050565b8161077c338261085d565b151561078757600080fd5b610792858585610483565b61079e85858585610ae6565b15156107a957600080fd5b5050505050565b600160a060020a03918216600090815260066020908152604080832093909416825291909152205460ff1690565b600054600160a060020a03163314610822576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161057690610fcf565b600160a060020a0381161561085a576000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b50565b60008061086983610622565b905080600160a060020a031684600160a060020a031614806108a4575083600160a060020a031661089984610372565b600160a060020a0316145b806108b457506108b481856107b0565b949350505050565b81600160a060020a03166108cf82610622565b600160a060020a0316146108e257600080fd5b600081815260046020526040902054600160a060020a0316156105c957600081815260046020526040808220805473ffffffffffffffffffffffffffffffffffffffff1916905551600160a060020a038416907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610765908590610fdf565b81600160a060020a031661097582610622565b600160a060020a03161461098857600080fd5b600160a060020a0382166000908152600560205260409020546109ac906001610bec565b600160a060020a03909216600090815260056020908152604080832094909455918152600390915220805473ffffffffffffffffffffffffffffffffffffffff19169055565b600081815260036020526040902054600160a060020a031615610a1457600080fd5b6000818152600360209081526040808320805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03871690811790915583526005909152902054610a63906001610bfe565b600160a060020a0390921660009081526005602052604090209190915550565b600160a060020a0382161515610a9857600080fd5b610aa282826109f2565b81600160a060020a03166000600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516107659190610fdf565b600080610af285610c14565b1515610b015760019150610be3565b6040517ff0b9e5ba000000000000000000000000000000000000000000000000000000008152600160a060020a0386169063f0b9e5ba90610b4a90899088908890600401610f6a565b602060405180830381600087803b158015610b6457600080fd5b505af1158015610b78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b9c9190810190610e1e565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1981167ff0b9e5ba0000000000000000000000000000000000000000000000000000000014925090505b50949350505050565b600082821115610bf857fe5b50900390565b600082820183811015610c0d57fe5b9392505050565b6000903b1190565b6000610c0d8235611040565b6000610c0d823561104c565b6000610c0d8251611054565b6000601f82018313610c5157600080fd5b8135610c64610c5f82611014565b610fed565b91508082526020830160208301858383011115610c8057600080fd5b610c8b838284611076565b50505092915050565b6000610c0d8235611051565b600060208284031215610cb257600080fd5b60006108b48484610c1c565b60008060408385031215610cd157600080fd5b6000610cdd8585610c1c565b9250506020610cee85828601610c1c565b9150509250929050565b600080600060608486031215610d0d57600080fd5b6000610d198686610c1c565b9350506020610d2a86828701610c1c565b9250506040610d3b86828701610c94565b9150509250925092565b60008060008060808587031215610d5b57600080fd5b6000610d678787610c1c565b9450506020610d7887828801610c1c565b9350506040610d8987828801610c94565b925050606085013567ffffffffffffffff811115610da657600080fd5b610db287828801610c40565b91505092959194509250565b60008060408385031215610dd157600080fd5b6000610ddd8585610c1c565b9250506020610cee85828601610c28565b60008060408385031215610e0157600080fd5b6000610e0d8585610c1c565b9250506020610cee85828601610c94565b600060208284031215610e3057600080fd5b60006108b48484610c34565b600060208284031215610e4e57600080fd5b60006108b48484610c94565b610e6381611040565b82525050565b610e638161104c565b6000610e7d8261103c565b808452610e91816020860160208601611082565b610e9a816110ae565b9093016020019392505050565b602281527f546f6b656e207769746820746f6b656e496420616c726561647920657869737460208201527f732e000000000000000000000000000000000000000000000000000000000000604082015260600190565b603381527f4f6e6c7920636f6e7472616374206f776e657220697320616c6c6f776564207460208201527f6f2063616c6c2074686973206d6574686f642e00000000000000000000000000604082015260600190565b610e6381611051565b602081016106468284610e5a565b60608101610f788286610e5a565b610f856020830185610f53565b8181036040830152610f978184610e72565b95945050505050565b602081016106468284610e69565b60208082528101610c0d8184610e72565b6020808252810161064681610ea7565b6020808252810161064681610efd565b602081016106468284610f53565b60405181810167ffffffffffffffff8111828210171561100c57600080fd5b604052919050565b600067ffffffffffffffff82111561102b57600080fd5b506020601f91909101601f19160190565b5190565b600160a060020a031690565b151590565b90565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690565b82818337506000910152565b60005b8381101561109d578181015183820152602001611085565b838111156105ff5750506000910152565b601f01601f1916905600a265627a7a72305820dca99ed080138e68d1f26208ce796f211cfc01d8c3da8021f8ee95e54f4b4d2d6c6578706572696d656e74616cf50037", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH3 0x11 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD PUSH3 0x1376 CODESIZE SUB DUP1 PUSH3 0x1376 DUP4 CODECOPY DUP2 ADD DUP1 PUSH1 0x40 MSTORE PUSH3 0x37 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH3 0x184 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB NOT AND CALLER OR SWAP1 SSTORE DUP2 MLOAD DUP3 SWAP1 DUP3 SWAP1 PUSH3 0x62 SWAP1 PUSH1 0x1 SWAP1 PUSH1 0x20 DUP6 ADD SWAP1 PUSH3 0x83 JUMP JUMPDEST POP DUP1 MLOAD PUSH3 0x78 SWAP1 PUSH1 0x2 SWAP1 PUSH1 0x20 DUP5 ADD SWAP1 PUSH3 0x83 JUMP JUMPDEST POP POP POP POP POP PUSH3 0x274 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 0xC6 JUMPI DUP1 MLOAD PUSH1 0xFF NOT AND DUP4 DUP1 ADD OR DUP6 SSTORE PUSH3 0xF6 JUMP JUMPDEST DUP3 DUP1 ADD PUSH1 0x1 ADD DUP6 SSTORE DUP3 ISZERO PUSH3 0xF6 JUMPI SWAP2 DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH3 0xF6 JUMPI DUP3 MLOAD DUP3 SSTORE SWAP2 PUSH1 0x20 ADD SWAP2 SWAP1 PUSH1 0x1 ADD SWAP1 PUSH3 0xD9 JUMP JUMPDEST POP PUSH3 0x104 SWAP3 SWAP2 POP PUSH3 0x108 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH3 0x125 SWAP2 SWAP1 JUMPDEST DUP1 DUP3 GT ISZERO PUSH3 0x104 JUMPI PUSH1 0x0 DUP2 SSTORE PUSH1 0x1 ADD PUSH3 0x10F JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH3 0x13A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 MLOAD PUSH3 0x151 PUSH3 0x14B DUP3 PUSH3 0x219 JUMP JUMPDEST PUSH3 0x1F2 JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH3 0x16E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH3 0x17B DUP4 DUP3 DUP5 PUSH3 0x241 JUMP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH3 0x198 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP3 MLOAD PUSH1 0x1 PUSH1 0x40 PUSH1 0x2 EXP SUB DUP2 GT ISZERO PUSH3 0x1AF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH3 0x1BD DUP6 DUP3 DUP7 ADD PUSH3 0x128 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 DUP4 ADD MLOAD PUSH1 0x1 PUSH1 0x40 PUSH1 0x2 EXP SUB DUP2 GT ISZERO PUSH3 0x1DA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH3 0x1E8 DUP6 DUP3 DUP7 ADD PUSH3 0x128 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 0x211 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 0x230 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH1 0x1F NOT AND ADD SWAP1 JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH3 0x25E JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH3 0x244 JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH3 0x26E JUMPI PUSH1 0x0 DUP5 DUP5 ADD MSTORE JUMPDEST POP POP POP POP JUMP JUMPDEST PUSH2 0x10F2 DUP1 PUSH3 0x284 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0xDA JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x6FDDE03 DUP2 EQ PUSH2 0xDF JUMPI DUP1 PUSH4 0x81812FC EQ PUSH2 0x10A JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x137 JUMPI DUP1 PUSH4 0x23B872DD EQ PUSH2 0x159 JUMPI DUP1 PUSH4 0x40C10F19 EQ PUSH2 0x179 JUMPI DUP1 PUSH4 0x42842E0E EQ PUSH2 0x199 JUMPI DUP1 PUSH4 0x4F558E79 EQ PUSH2 0x1B9 JUMPI DUP1 PUSH4 0x6352211E EQ PUSH2 0x1E6 JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x206 JUMPI DUP1 PUSH4 0x8DA5CB5B EQ PUSH2 0x233 JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x248 JUMPI DUP1 PUSH4 0xA22CB465 EQ PUSH2 0x25D JUMPI DUP1 PUSH4 0xB88D4FDE EQ PUSH2 0x27D JUMPI DUP1 PUSH4 0xE985E9C5 EQ PUSH2 0x29D JUMPI DUP1 PUSH4 0xF2FDE38B EQ PUSH2 0x2BD JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xEB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xF4 PUSH2 0x2DD JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x101 SWAP2 SWAP1 PUSH2 0xFAE JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x116 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x12A PUSH2 0x125 CALLDATASIZE PUSH1 0x4 PUSH2 0xE3C JUMP JUMPDEST PUSH2 0x372 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x101 SWAP2 SWAP1 PUSH2 0xF5C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x143 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x157 PUSH2 0x152 CALLDATASIZE PUSH1 0x4 PUSH2 0xDEE JUMP JUMPDEST PUSH2 0x38D JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x165 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x157 PUSH2 0x174 CALLDATASIZE PUSH1 0x4 PUSH2 0xCF8 JUMP JUMPDEST PUSH2 0x483 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x185 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x157 PUSH2 0x194 CALLDATASIZE PUSH1 0x4 PUSH2 0xDEE JUMP JUMPDEST PUSH2 0x532 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1A5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x157 PUSH2 0x1B4 CALLDATASIZE PUSH1 0x4 PUSH2 0xCF8 JUMP JUMPDEST PUSH2 0x5CD JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1C5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1D9 PUSH2 0x1D4 CALLDATASIZE PUSH1 0x4 PUSH2 0xE3C JUMP JUMPDEST PUSH2 0x605 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x101 SWAP2 SWAP1 PUSH2 0xFA0 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1F2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x12A PUSH2 0x201 CALLDATASIZE PUSH1 0x4 PUSH2 0xE3C JUMP JUMPDEST PUSH2 0x622 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x212 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x226 PUSH2 0x221 CALLDATASIZE PUSH1 0x4 PUSH2 0xCA0 JUMP JUMPDEST PUSH2 0x64C JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x101 SWAP2 SWAP1 PUSH2 0xFDF JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x23F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x12A PUSH2 0x67F JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x254 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xF4 PUSH2 0x68E JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x269 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x157 PUSH2 0x278 CALLDATASIZE PUSH1 0x4 PUSH2 0xDBE JUMP JUMPDEST PUSH2 0x6EC JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x289 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x157 PUSH2 0x298 CALLDATASIZE PUSH1 0x4 PUSH2 0xD45 JUMP JUMPDEST PUSH2 0x771 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2A9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1D9 PUSH2 0x2B8 CALLDATASIZE PUSH1 0x4 PUSH2 0xCBE JUMP JUMPDEST PUSH2 0x7B0 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2C9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x157 PUSH2 0x2D8 CALLDATASIZE PUSH1 0x4 PUSH2 0xCA0 JUMP JUMPDEST PUSH2 0x7DE JUMP JUMPDEST PUSH1 0x1 DUP1 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 PUSH1 0x1F PUSH1 0x2 PUSH1 0x0 NOT PUSH2 0x100 DUP8 DUP10 AND ISZERO MUL ADD SWAP1 SWAP6 AND SWAP5 SWAP1 SWAP5 DIV SWAP4 DUP5 ADD DUP2 SWAP1 DIV DUP2 MUL DUP3 ADD DUP2 ADD SWAP1 SWAP3 MSTORE DUP3 DUP2 MSTORE PUSH1 0x60 SWAP4 SWAP1 SWAP3 SWAP1 SWAP2 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0x368 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x33D JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x368 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 0x34B JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP POP POP SWAP1 POP SWAP1 JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x398 DUP3 PUSH2 0x622 JUMP JUMPDEST SWAP1 POP PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 DUP2 AND SWAP1 DUP3 AND EQ ISZERO PUSH2 0x3B3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST CALLER PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND EQ DUP1 PUSH2 0x3CF JUMPI POP PUSH2 0x3CF DUP2 CALLER PUSH2 0x7B0 JUMP JUMPDEST ISZERO ISZERO PUSH2 0x3DA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x3E5 DUP4 PUSH2 0x372 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ ISZERO DUP1 PUSH2 0x403 JUMPI POP PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND ISZERO ISZERO JUMPDEST ISZERO PUSH2 0x47E JUMPI PUSH1 0x0 DUP3 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP7 DUP2 AND SWAP2 DUP3 OR SWAP1 SWAP3 SSTORE SWAP2 MLOAD SWAP1 DUP4 AND SWAP1 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP1 PUSH2 0x475 SWAP1 DUP7 SWAP1 PUSH2 0xFDF JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 JUMPDEST POP POP POP JUMP JUMPDEST DUP1 PUSH2 0x48E CALLER DUP3 PUSH2 0x85D JUMP JUMPDEST ISZERO ISZERO PUSH2 0x499 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP5 AND ISZERO ISZERO PUSH2 0x4AE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND ISZERO ISZERO PUSH2 0x4C3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4CD DUP5 DUP4 PUSH2 0x8BC JUMP JUMPDEST PUSH2 0x4D7 DUP5 DUP4 PUSH2 0x962 JUMP JUMPDEST PUSH2 0x4E1 DUP4 DUP4 PUSH2 0x9F2 JUMP JUMPDEST DUP3 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP5 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP5 PUSH1 0x40 MLOAD PUSH2 0x524 SWAP2 SWAP1 PUSH2 0xFDF JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND CALLER EQ PUSH2 0x57F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x576 SWAP1 PUSH2 0xFCF JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH2 0x588 DUP2 PUSH2 0x605 JUMP JUMPDEST ISZERO PUSH2 0x5BF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x576 SWAP1 PUSH2 0xFBF JUMP JUMPDEST PUSH2 0x5C9 DUP3 DUP3 PUSH2 0xA83 JUMP JUMPDEST POP POP JUMP JUMPDEST DUP1 PUSH2 0x5D8 CALLER DUP3 PUSH2 0x85D JUMP JUMPDEST ISZERO ISZERO PUSH2 0x5E3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x5FF DUP5 DUP5 DUP5 PUSH1 0x20 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE POP PUSH2 0x771 JUMP JUMPDEST POP POP POP POP JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND ISZERO ISZERO SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP1 ISZERO ISZERO PUSH2 0x646 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND ISZERO ISZERO PUSH2 0x663 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP2 JUMP JUMPDEST PUSH1 0x2 DUP1 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 PUSH1 0x1F PUSH1 0x0 NOT PUSH2 0x100 PUSH1 0x1 DUP8 AND ISZERO MUL ADD SWAP1 SWAP5 AND DUP6 SWAP1 DIV SWAP4 DUP5 ADD DUP2 SWAP1 DIV DUP2 MUL DUP3 ADD DUP2 ADD SWAP1 SWAP3 MSTORE DUP3 DUP2 MSTORE PUSH1 0x60 SWAP4 SWAP1 SWAP3 SWAP1 SWAP2 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0x368 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x33D JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x368 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND CALLER EQ ISZERO PUSH2 0x702 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP8 AND DUP1 DUP6 MSTORE SWAP3 MSTORE SWAP2 DUP3 SWAP1 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT AND DUP6 ISZERO ISZERO OR SWAP1 SSTORE SWAP1 MLOAD SWAP1 SWAP2 SWAP1 PUSH32 0x17307EAB39AB6107E8899845AD3D59BD9653F200F220920489CA2B5937696C31 SWAP1 PUSH2 0x765 SWAP1 DUP6 SWAP1 PUSH2 0xFA0 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP JUMP JUMPDEST DUP2 PUSH2 0x77C CALLER DUP3 PUSH2 0x85D JUMP JUMPDEST ISZERO ISZERO PUSH2 0x787 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x792 DUP6 DUP6 DUP6 PUSH2 0x483 JUMP JUMPDEST PUSH2 0x79E DUP6 DUP6 DUP6 DUP6 PUSH2 0xAE6 JUMP JUMPDEST ISZERO ISZERO PUSH2 0x7A9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP POP POP POP POP JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP2 DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 SWAP1 SWAP5 AND DUP3 MSTORE SWAP2 SWAP1 SWAP2 MSTORE KECCAK256 SLOAD PUSH1 0xFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND CALLER EQ PUSH2 0x822 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x576 SWAP1 PUSH2 0xFCF JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP2 AND ISZERO PUSH2 0x85A JUMPI PUSH1 0x0 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND OR SWAP1 SSTORE JUMPDEST POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0x869 DUP4 PUSH2 0x622 JUMP JUMPDEST SWAP1 POP DUP1 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP5 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ DUP1 PUSH2 0x8A4 JUMPI POP DUP4 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH2 0x899 DUP5 PUSH2 0x372 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ JUMPDEST DUP1 PUSH2 0x8B4 JUMPI POP PUSH2 0x8B4 DUP2 DUP6 PUSH2 0x7B0 JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH2 0x8CF DUP3 PUSH2 0x622 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ PUSH2 0x8E2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND ISZERO PUSH2 0x5C9 JUMPI PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND SWAP1 SSTORE MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP5 AND SWAP1 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP1 PUSH2 0x765 SWAP1 DUP6 SWAP1 PUSH2 0xFDF JUMP JUMPDEST DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH2 0x975 DUP3 PUSH2 0x622 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ PUSH2 0x988 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x9AC SWAP1 PUSH1 0x1 PUSH2 0xBEC JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 SWAP1 SWAP5 SSTORE SWAP2 DUP2 MSTORE PUSH1 0x3 SWAP1 SWAP2 MSTORE KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND ISZERO PUSH2 0xA14 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP8 AND SWAP1 DUP2 OR SWAP1 SWAP2 SSTORE DUP4 MSTORE PUSH1 0x5 SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD PUSH2 0xA63 SWAP1 PUSH1 0x1 PUSH2 0xBFE JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SWAP2 SWAP1 SWAP2 SSTORE POP JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND ISZERO ISZERO PUSH2 0xA98 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xAA2 DUP3 DUP3 PUSH2 0x9F2 JUMP JUMPDEST DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x0 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP4 PUSH1 0x40 MLOAD PUSH2 0x765 SWAP2 SWAP1 PUSH2 0xFDF JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0xAF2 DUP6 PUSH2 0xC14 JUMP JUMPDEST ISZERO ISZERO PUSH2 0xB01 JUMPI PUSH1 0x1 SWAP2 POP PUSH2 0xBE3 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0xF0B9E5BA00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP7 AND SWAP1 PUSH4 0xF0B9E5BA SWAP1 PUSH2 0xB4A SWAP1 DUP10 SWAP1 DUP9 SWAP1 DUP9 SWAP1 PUSH1 0x4 ADD PUSH2 0xF6A JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0xB64 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0xB78 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 0xB9C SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0xE1E JUMP JUMPDEST PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT DUP2 AND PUSH32 0xF0B9E5BA00000000000000000000000000000000000000000000000000000000 EQ SWAP3 POP SWAP1 POP JUMPDEST POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 GT ISZERO PUSH2 0xBF8 JUMPI INVALID JUMPDEST POP SWAP1 SUB SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 ADD DUP4 DUP2 LT ISZERO PUSH2 0xC0D JUMPI INVALID JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x0 SWAP1 EXTCODESIZE GT SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC0D DUP3 CALLDATALOAD PUSH2 0x1040 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC0D DUP3 CALLDATALOAD PUSH2 0x104C JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC0D DUP3 MLOAD PUSH2 0x1054 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xC51 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xC64 PUSH2 0xC5F DUP3 PUSH2 0x1014 JUMP JUMPDEST PUSH2 0xFED JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH2 0xC80 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xC8B DUP4 DUP3 DUP5 PUSH2 0x1076 JUMP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC0D DUP3 CALLDATALOAD PUSH2 0x1051 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xCB2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x8B4 DUP5 DUP5 PUSH2 0xC1C JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0xCD1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xCDD DUP6 DUP6 PUSH2 0xC1C JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0xCEE DUP6 DUP3 DUP7 ADD PUSH2 0xC1C JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0xD0D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xD19 DUP7 DUP7 PUSH2 0xC1C JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0xD2A DUP7 DUP3 DUP8 ADD PUSH2 0xC1C JUMP JUMPDEST SWAP3 POP POP PUSH1 0x40 PUSH2 0xD3B DUP7 DUP3 DUP8 ADD PUSH2 0xC94 JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 POP SWAP3 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x80 DUP6 DUP8 SUB SLT ISZERO PUSH2 0xD5B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xD67 DUP8 DUP8 PUSH2 0xC1C JUMP JUMPDEST SWAP5 POP POP PUSH1 0x20 PUSH2 0xD78 DUP8 DUP3 DUP9 ADD PUSH2 0xC1C JUMP JUMPDEST SWAP4 POP POP PUSH1 0x40 PUSH2 0xD89 DUP8 DUP3 DUP9 ADD PUSH2 0xC94 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x60 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xDA6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xDB2 DUP8 DUP3 DUP9 ADD PUSH2 0xC40 JUMP JUMPDEST SWAP2 POP POP SWAP3 SWAP6 SWAP2 SWAP5 POP SWAP3 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0xDD1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xDDD DUP6 DUP6 PUSH2 0xC1C JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0xCEE DUP6 DUP3 DUP7 ADD PUSH2 0xC28 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0xE01 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xE0D DUP6 DUP6 PUSH2 0xC1C JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0xCEE DUP6 DUP3 DUP7 ADD PUSH2 0xC94 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xE30 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x8B4 DUP5 DUP5 PUSH2 0xC34 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xE4E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x8B4 DUP5 DUP5 PUSH2 0xC94 JUMP JUMPDEST PUSH2 0xE63 DUP2 PUSH2 0x1040 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH2 0xE63 DUP2 PUSH2 0x104C JUMP JUMPDEST PUSH1 0x0 PUSH2 0xE7D DUP3 PUSH2 0x103C JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH2 0xE91 DUP2 PUSH1 0x20 DUP7 ADD PUSH1 0x20 DUP7 ADD PUSH2 0x1082 JUMP JUMPDEST PUSH2 0xE9A DUP2 PUSH2 0x10AE JUMP JUMPDEST SWAP1 SWAP4 ADD PUSH1 0x20 ADD SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x22 DUP2 MSTORE PUSH32 0x546F6B656E207769746820746F6B656E496420616C7265616479206578697374 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x732E000000000000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x33 DUP2 MSTORE PUSH32 0x4F6E6C7920636F6E7472616374206F776E657220697320616C6C6F7765642074 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x6F2063616C6C2074686973206D6574686F642E00000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH2 0xE63 DUP2 PUSH2 0x1051 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x646 DUP3 DUP5 PUSH2 0xE5A JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0xF78 DUP3 DUP7 PUSH2 0xE5A JUMP JUMPDEST PUSH2 0xF85 PUSH1 0x20 DUP4 ADD DUP6 PUSH2 0xF53 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x40 DUP4 ADD MSTORE PUSH2 0xF97 DUP2 DUP5 PUSH2 0xE72 JUMP JUMPDEST SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x646 DUP3 DUP5 PUSH2 0xE69 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xC0D DUP2 DUP5 PUSH2 0xE72 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x646 DUP2 PUSH2 0xEA7 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x646 DUP2 PUSH2 0xEFD JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x646 DUP3 DUP5 PUSH2 0xF53 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0x100C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x102B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH1 0x1F NOT AND ADD SWAP1 JUMP JUMPDEST MLOAD SWAP1 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP1 JUMP JUMPDEST ISZERO ISZERO SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND SWAP1 JUMP JUMPDEST DUP3 DUP2 DUP4 CALLDATACOPY POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x109D JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x1085 JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0x5FF JUMPI POP POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x1F ADD PUSH1 0x1F NOT AND SWAP1 JUMP STOP LOG2 PUSH6 0x627A7A723058 KECCAK256 0xdc 0xa9 SWAP15 0xd0 DUP1 SGT DUP15 PUSH9 0xD1F26208CE796F211C 0xfc ADD 0xd8 0xc3 0xda DUP1 0x21 0xf8 0xee SWAP6 0xe5 0x4f 0x4b 0x4d 0x2d PUSH13 0x6578706572696D656E74616CF5 STOP CALLDATACOPY ", - "sourceMap": "734:822:0:-;;;950:118;8:9:-1;5:2;;;30:1;27;20:12;5:2;950:118:0;;;;;;;;;;;;;;;;;;;;;;;;363:5:5;:18;;-1:-1:-1;;;;;;363:18:5;371:10;363:18;;;2885:13:1;;1048:4:0;;1054:6;;2885:13:1;;363:18:5;;2885:13:1;;;;;:::i;:::-;-1:-1:-1;2908:17:1;;;;:7;;:17;;;;;:::i;:::-;;2788:144;;950:118:0;;734:822;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;734:822:0;;;-1:-1:-1;734:822:0;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::o;6:444:-1:-;;112:4;100:17;;96:27;-1:-1;86:2;;137:1;134;127:12;86:2;167:6;161:13;189:65;204:49;246:6;204:49;;;189:65;;;180:74;;274:6;267:5;260:21;310:4;302:6;298:17;343:4;336:5;332:16;378:3;369:6;364:3;360:16;357:25;354:2;;;395:1;392;385:12;354:2;405:39;437:6;432:3;427;405:39;;;79:371;;;;;;;;458:597;;;610:2;598:9;589:7;585:23;581:32;578:2;;;626:1;623;616:12;578:2;661:24;;-1:-1;;;;;694:30;;691:2;;;737:1;734;727:12;691:2;757:74;823:7;814:6;803:9;799:22;757:74;;;747:84;;640:197;889:2;878:9;874:18;868:25;-1:-1;;;;;905:6;902:30;899:2;;;945:1;942;935:12;899:2;965:74;1031:7;1022:6;1011:9;1007:22;965:74;;;955:84;;847:198;572:483;;;;;;1062:256;1124:2;1118:9;1150:17;;;-1:-1;;;;;1210:34;;1246:22;;;1207:62;1204:2;;;1282:1;1279;1272:12;1204:2;1298;1291:22;1102:216;;-1:-1;1102:216;1325:259;;-1:-1;;;;;1461:6;1458:30;1455:2;;;1501:1;1498;1491:12;1455:2;-1:-1;1574:4;1545;1522:17;;;;-1:-1;;1518:33;1564:15;;1392:192;1592:268;1657:1;1664:101;1678:6;1675:1;1672:13;1664:101;;;1745:11;;;1739:18;1726:11;;;1719:39;1700:2;1693:10;1664:101;;;1780:6;1777:1;1774:13;1771:2;;;1845:1;1836:6;1831:3;1827:16;1820:27;1771:2;1641:219;;;;;;734:822:0;;;;;;" + "object": "0x60806040523480156200001157600080fd5b506040516200147b3803806200147b833981018060405262000037919081019062000184565b60008054600160a060020a031916331790558151829082906200006290600190602085019062000083565b5080516200007890600290602084019062000083565b505050505062000274565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620000c657805160ff1916838001178555620000f6565b82800160010185558215620000f6579182015b82811115620000f6578251825591602001919060010190620000d9565b506200010492915062000108565b5090565b6200012591905b808211156200010457600081556001016200010f565b90565b6000601f820183136200013a57600080fd5b8151620001516200014b8262000219565b620001f2565b915080825260208301602083018583830111156200016e57600080fd5b6200017b83828462000241565b50505092915050565b600080604083850312156200019857600080fd5b82516001604060020a03811115620001af57600080fd5b620001bd8582860162000128565b92505060208301516001604060020a03811115620001da57600080fd5b620001e88582860162000128565b9150509250929050565b6040518181016001604060020a03811182821017156200021157600080fd5b604052919050565b60006001604060020a038211156200023057600080fd5b506020601f91909101601f19160190565b60005b838110156200025e57818101518382015260200162000244565b838111156200026e576000848401525b50505050565b6111f780620002846000396000f3006080604052600436106100e55763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde0381146100ea578063081812fc14610115578063095ea7b31461014257806323b872dd1461016457806340c10f191461018457806342842e0e146101a45780634f558e79146101c45780636352211e146101f157806370a08231146102115780638da5cb5b1461023e57806395d89b41146102535780639dc29fac14610268578063a22cb46514610288578063b88d4fde146102a8578063e985e9c5146102c8578063f2fde38b146102e8575b600080fd5b3480156100f657600080fd5b506100ff610308565b60405161010c91906110a3565b60405180910390f35b34801561012157600080fd5b50610135610130366004610edb565b61039d565b60405161010c9190611051565b34801561014e57600080fd5b5061016261015d366004610e8d565b6103b8565b005b34801561017057600080fd5b5061016261017f366004610d97565b6104ae565b34801561019057600080fd5b5061016261019f366004610e8d565b61055d565b3480156101b057600080fd5b506101626101bf366004610d97565b6105ca565b3480156101d057600080fd5b506101e46101df366004610edb565b610602565b60405161010c9190611095565b3480156101fd57600080fd5b5061013561020c366004610edb565b61061f565b34801561021d57600080fd5b5061023161022c366004610d3f565b610649565b60405161010c91906110e4565b34801561024a57600080fd5b5061013561067c565b34801561025f57600080fd5b506100ff61068b565b34801561027457600080fd5b50610162610283366004610e8d565b6106e9565b34801561029457600080fd5b506101626102a3366004610e5d565b61074a565b3480156102b457600080fd5b506101626102c3366004610de4565b6107cf565b3480156102d457600080fd5b506101e46102e3366004610d5d565b61080e565b3480156102f457600080fd5b50610162610303366004610d3f565b61083c565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156103935780601f1061036857610100808354040283529160200191610393565b820191906000526020600020905b81548152906001019060200180831161037657829003601f168201915b5050505050905090565b600090815260046020526040902054600160a060020a031690565b60006103c38261061f565b9050600160a060020a0383811690821614156103de57600080fd5b33600160a060020a03821614806103fa57506103fa813361080e565b151561040557600080fd5b60006104108361039d565b600160a060020a031614158061042e5750600160a060020a03831615155b156104a95760008281526004602052604090819020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a038681169182179092559151908316907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906104a09086906110e4565b60405180910390a35b505050565b806104b933826108a4565b15156104c457600080fd5b600160a060020a03841615156104d957600080fd5b600160a060020a03831615156104ee57600080fd5b6104f88483610903565b61050284836109a9565b61050c8383610a39565b82600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161054f91906110e4565b60405180910390a350505050565b600054600160a060020a031633146105935760405160e560020a62461bcd02815260040161058a906110c4565b60405180910390fd5b61059c81610602565b156105bc5760405160e560020a62461bcd02815260040161058a906110b4565b6105c68282610aca565b5050565b806105d533826108a4565b15156105e057600080fd5b6105fc84848460206040519081016040528060008152506107cf565b50505050565b600090815260036020526040902054600160a060020a0316151590565b600081815260036020526040812054600160a060020a031680151561064357600080fd5b92915050565b6000600160a060020a038216151561066057600080fd5b50600160a060020a031660009081526005602052604090205490565b600054600160a060020a031681565b60028054604080516020601f60001961010060018716150201909416859004938401819004810282018101909252828152606093909290918301828280156103935780601f1061036857610100808354040283529160200191610393565b600054600160a060020a031633146107165760405160e560020a62461bcd02815260040161058a906110c4565b61071f81610602565b15156107405760405160e560020a62461bcd02815260040161058a906110d4565b6105c68282610b2d565b600160a060020a03821633141561076057600080fd5b336000818152600660209081526040808320600160a060020a038716808552925291829020805460ff191685151517905590519091907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31906107c3908590611095565b60405180910390a35050565b816107da33826108a4565b15156107e557600080fd5b6107f08585856104ae565b6107fc85858585610b85565b151561080757600080fd5b5050505050565b600160a060020a03918216600090815260066020908152604080832093909416825291909152205460ff1690565b600054600160a060020a031633146108695760405160e560020a62461bcd02815260040161058a906110c4565b600160a060020a038116156108a1576000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b50565b6000806108b08361061f565b905080600160a060020a031684600160a060020a031614806108eb575083600160a060020a03166108e08461039d565b600160a060020a0316145b806108fb57506108fb818561080e565b949350505050565b81600160a060020a03166109168261061f565b600160a060020a03161461092957600080fd5b600081815260046020526040902054600160a060020a0316156105c657600081815260046020526040808220805473ffffffffffffffffffffffffffffffffffffffff1916905551600160a060020a038416907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906107c39085906110e4565b81600160a060020a03166109bc8261061f565b600160a060020a0316146109cf57600080fd5b600160a060020a0382166000908152600560205260409020546109f3906001610c8b565b600160a060020a03909216600090815260056020908152604080832094909455918152600390915220805473ffffffffffffffffffffffffffffffffffffffff19169055565b600081815260036020526040902054600160a060020a031615610a5b57600080fd5b6000818152600360209081526040808320805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03871690811790915583526005909152902054610aaa906001610c9d565b600160a060020a0390921660009081526005602052604090209190915550565b600160a060020a0382161515610adf57600080fd5b610ae98282610a39565b81600160a060020a03166000600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516107c391906110e4565b610b378282610903565b610b4182826109a9565b6000600160a060020a031682600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516107c391906110e4565b600080610b9185610cb3565b1515610ba05760019150610c82565b6040517ff0b9e5ba000000000000000000000000000000000000000000000000000000008152600160a060020a0386169063f0b9e5ba90610be99089908890889060040161105f565b602060405180830381600087803b158015610c0357600080fd5b505af1158015610c17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c3b9190810190610ebd565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1981167ff0b9e5ba0000000000000000000000000000000000000000000000000000000014925090505b50949350505050565b600082821115610c9757fe5b50900390565b600082820183811015610cac57fe5b9392505050565b6000903b1190565b6000610cac8235611145565b6000610cac8235611151565b6000610cac8251611159565b6000601f82018313610cf057600080fd5b8135610d03610cfe82611119565b6110f2565b91508082526020830160208301858383011115610d1f57600080fd5b610d2a83828461117b565b50505092915050565b6000610cac8235611156565b600060208284031215610d5157600080fd5b60006108fb8484610cbb565b60008060408385031215610d7057600080fd5b6000610d7c8585610cbb565b9250506020610d8d85828601610cbb565b9150509250929050565b600080600060608486031215610dac57600080fd5b6000610db88686610cbb565b9350506020610dc986828701610cbb565b9250506040610dda86828701610d33565b9150509250925092565b60008060008060808587031215610dfa57600080fd5b6000610e068787610cbb565b9450506020610e1787828801610cbb565b9350506040610e2887828801610d33565b925050606085013567ffffffffffffffff811115610e4557600080fd5b610e5187828801610cdf565b91505092959194509250565b60008060408385031215610e7057600080fd5b6000610e7c8585610cbb565b9250506020610d8d85828601610cc7565b60008060408385031215610ea057600080fd5b6000610eac8585610cbb565b9250506020610d8d85828601610d33565b600060208284031215610ecf57600080fd5b60006108fb8484610cd3565b600060208284031215610eed57600080fd5b60006108fb8484610d33565b610f0281611145565b82525050565b610f0281611151565b6000610f1c82611141565b808452610f30816020860160208601611187565b610f39816111b3565b9093016020019392505050565b602281527f546f6b656e207769746820746f6b656e496420616c726561647920657869737460208201527f732e000000000000000000000000000000000000000000000000000000000000604082015260600190565b603381527f4f6e6c7920636f6e7472616374206f776e657220697320616c6c6f776564207460208201527f6f2063616c6c2074686973206d6574686f642e00000000000000000000000000604082015260600190565b602281527f546f6b656e207769746820746f6b656e496420646f6573206e6f74206578697360208201527f742e000000000000000000000000000000000000000000000000000000000000604082015260600190565b610f0281611156565b602081016106438284610ef9565b6060810161106d8286610ef9565b61107a6020830185611048565b818103604083015261108c8184610f11565b95945050505050565b602081016106438284610f08565b60208082528101610cac8184610f11565b6020808252810161064381610f46565b6020808252810161064381610f9c565b6020808252810161064381610ff2565b602081016106438284611048565b60405181810167ffffffffffffffff8111828210171561111157600080fd5b604052919050565b600067ffffffffffffffff82111561113057600080fd5b506020601f91909101601f19160190565b5190565b600160a060020a031690565b151590565b90565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690565b82818337506000910152565b60005b838110156111a257818101518382015260200161118a565b838111156105fc5750506000910152565b601f01601f1916905600a265627a7a723058200ffaaad094e9af24e42bef0c99c0305afe66dc20df0f57455ad67e1a0741d7c76c6578706572696d656e74616cf50037", + "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH3 0x11 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD PUSH3 0x147B CODESIZE SUB DUP1 PUSH3 0x147B DUP4 CODECOPY DUP2 ADD DUP1 PUSH1 0x40 MSTORE PUSH3 0x37 SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH3 0x184 JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB NOT AND CALLER OR SWAP1 SSTORE DUP2 MLOAD DUP3 SWAP1 DUP3 SWAP1 PUSH3 0x62 SWAP1 PUSH1 0x1 SWAP1 PUSH1 0x20 DUP6 ADD SWAP1 PUSH3 0x83 JUMP JUMPDEST POP DUP1 MLOAD PUSH3 0x78 SWAP1 PUSH1 0x2 SWAP1 PUSH1 0x20 DUP5 ADD SWAP1 PUSH3 0x83 JUMP JUMPDEST POP POP POP POP POP PUSH3 0x274 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 0xC6 JUMPI DUP1 MLOAD PUSH1 0xFF NOT AND DUP4 DUP1 ADD OR DUP6 SSTORE PUSH3 0xF6 JUMP JUMPDEST DUP3 DUP1 ADD PUSH1 0x1 ADD DUP6 SSTORE DUP3 ISZERO PUSH3 0xF6 JUMPI SWAP2 DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH3 0xF6 JUMPI DUP3 MLOAD DUP3 SSTORE SWAP2 PUSH1 0x20 ADD SWAP2 SWAP1 PUSH1 0x1 ADD SWAP1 PUSH3 0xD9 JUMP JUMPDEST POP PUSH3 0x104 SWAP3 SWAP2 POP PUSH3 0x108 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH3 0x125 SWAP2 SWAP1 JUMPDEST DUP1 DUP3 GT ISZERO PUSH3 0x104 JUMPI PUSH1 0x0 DUP2 SSTORE PUSH1 0x1 ADD PUSH3 0x10F JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH3 0x13A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 MLOAD PUSH3 0x151 PUSH3 0x14B DUP3 PUSH3 0x219 JUMP JUMPDEST PUSH3 0x1F2 JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH3 0x16E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH3 0x17B DUP4 DUP3 DUP5 PUSH3 0x241 JUMP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH3 0x198 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP3 MLOAD PUSH1 0x1 PUSH1 0x40 PUSH1 0x2 EXP SUB DUP2 GT ISZERO PUSH3 0x1AF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH3 0x1BD DUP6 DUP3 DUP7 ADD PUSH3 0x128 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 DUP4 ADD MLOAD PUSH1 0x1 PUSH1 0x40 PUSH1 0x2 EXP SUB DUP2 GT ISZERO PUSH3 0x1DA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH3 0x1E8 DUP6 DUP3 DUP7 ADD PUSH3 0x128 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 0x211 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 0x230 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH1 0x1F NOT AND ADD SWAP1 JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH3 0x25E JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH3 0x244 JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH3 0x26E JUMPI PUSH1 0x0 DUP5 DUP5 ADD MSTORE JUMPDEST POP POP POP POP JUMP JUMPDEST PUSH2 0x11F7 DUP1 PUSH3 0x284 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0xE5 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x6FDDE03 DUP2 EQ PUSH2 0xEA JUMPI DUP1 PUSH4 0x81812FC EQ PUSH2 0x115 JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x142 JUMPI DUP1 PUSH4 0x23B872DD EQ PUSH2 0x164 JUMPI DUP1 PUSH4 0x40C10F19 EQ PUSH2 0x184 JUMPI DUP1 PUSH4 0x42842E0E EQ PUSH2 0x1A4 JUMPI DUP1 PUSH4 0x4F558E79 EQ PUSH2 0x1C4 JUMPI DUP1 PUSH4 0x6352211E EQ PUSH2 0x1F1 JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x211 JUMPI DUP1 PUSH4 0x8DA5CB5B EQ PUSH2 0x23E JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x253 JUMPI DUP1 PUSH4 0x9DC29FAC EQ PUSH2 0x268 JUMPI DUP1 PUSH4 0xA22CB465 EQ PUSH2 0x288 JUMPI DUP1 PUSH4 0xB88D4FDE EQ PUSH2 0x2A8 JUMPI DUP1 PUSH4 0xE985E9C5 EQ PUSH2 0x2C8 JUMPI DUP1 PUSH4 0xF2FDE38B EQ PUSH2 0x2E8 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xF6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xFF PUSH2 0x308 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x10C SWAP2 SWAP1 PUSH2 0x10A3 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x121 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x135 PUSH2 0x130 CALLDATASIZE PUSH1 0x4 PUSH2 0xEDB JUMP JUMPDEST PUSH2 0x39D JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x10C SWAP2 SWAP1 PUSH2 0x1051 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x14E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x15D CALLDATASIZE PUSH1 0x4 PUSH2 0xE8D JUMP JUMPDEST PUSH2 0x3B8 JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x170 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x17F CALLDATASIZE PUSH1 0x4 PUSH2 0xD97 JUMP JUMPDEST PUSH2 0x4AE JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x190 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x19F CALLDATASIZE PUSH1 0x4 PUSH2 0xE8D JUMP JUMPDEST PUSH2 0x55D JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1B0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x1BF CALLDATASIZE PUSH1 0x4 PUSH2 0xD97 JUMP JUMPDEST PUSH2 0x5CA JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1D0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1E4 PUSH2 0x1DF CALLDATASIZE PUSH1 0x4 PUSH2 0xEDB JUMP JUMPDEST PUSH2 0x602 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x10C SWAP2 SWAP1 PUSH2 0x1095 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1FD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x135 PUSH2 0x20C CALLDATASIZE PUSH1 0x4 PUSH2 0xEDB JUMP JUMPDEST PUSH2 0x61F JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x21D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x231 PUSH2 0x22C CALLDATASIZE PUSH1 0x4 PUSH2 0xD3F JUMP JUMPDEST PUSH2 0x649 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x10C SWAP2 SWAP1 PUSH2 0x10E4 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x24A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x135 PUSH2 0x67C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x25F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xFF PUSH2 0x68B JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x274 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x283 CALLDATASIZE PUSH1 0x4 PUSH2 0xE8D JUMP JUMPDEST PUSH2 0x6E9 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x294 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x2A3 CALLDATASIZE PUSH1 0x4 PUSH2 0xE5D JUMP JUMPDEST PUSH2 0x74A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2B4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x2C3 CALLDATASIZE PUSH1 0x4 PUSH2 0xDE4 JUMP JUMPDEST PUSH2 0x7CF JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2D4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1E4 PUSH2 0x2E3 CALLDATASIZE PUSH1 0x4 PUSH2 0xD5D JUMP JUMPDEST PUSH2 0x80E JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2F4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x303 CALLDATASIZE PUSH1 0x4 PUSH2 0xD3F JUMP JUMPDEST PUSH2 0x83C JUMP JUMPDEST PUSH1 0x1 DUP1 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 PUSH1 0x1F PUSH1 0x2 PUSH1 0x0 NOT PUSH2 0x100 DUP8 DUP10 AND ISZERO MUL ADD SWAP1 SWAP6 AND SWAP5 SWAP1 SWAP5 DIV SWAP4 DUP5 ADD DUP2 SWAP1 DIV DUP2 MUL DUP3 ADD DUP2 ADD SWAP1 SWAP3 MSTORE DUP3 DUP2 MSTORE PUSH1 0x60 SWAP4 SWAP1 SWAP3 SWAP1 SWAP2 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0x393 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x368 JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x393 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 0x376 JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP POP POP SWAP1 POP SWAP1 JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x3C3 DUP3 PUSH2 0x61F JUMP JUMPDEST SWAP1 POP PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 DUP2 AND SWAP1 DUP3 AND EQ ISZERO PUSH2 0x3DE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST CALLER PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND EQ DUP1 PUSH2 0x3FA JUMPI POP PUSH2 0x3FA DUP2 CALLER PUSH2 0x80E JUMP JUMPDEST ISZERO ISZERO PUSH2 0x405 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x410 DUP4 PUSH2 0x39D JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ ISZERO DUP1 PUSH2 0x42E JUMPI POP PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND ISZERO ISZERO JUMPDEST ISZERO PUSH2 0x4A9 JUMPI PUSH1 0x0 DUP3 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP7 DUP2 AND SWAP2 DUP3 OR SWAP1 SWAP3 SSTORE SWAP2 MLOAD SWAP1 DUP4 AND SWAP1 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP1 PUSH2 0x4A0 SWAP1 DUP7 SWAP1 PUSH2 0x10E4 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 JUMPDEST POP POP POP JUMP JUMPDEST DUP1 PUSH2 0x4B9 CALLER DUP3 PUSH2 0x8A4 JUMP JUMPDEST ISZERO ISZERO PUSH2 0x4C4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP5 AND ISZERO ISZERO PUSH2 0x4D9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND ISZERO ISZERO PUSH2 0x4EE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4F8 DUP5 DUP4 PUSH2 0x903 JUMP JUMPDEST PUSH2 0x502 DUP5 DUP4 PUSH2 0x9A9 JUMP JUMPDEST PUSH2 0x50C DUP4 DUP4 PUSH2 0xA39 JUMP JUMPDEST DUP3 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP5 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP5 PUSH1 0x40 MLOAD PUSH2 0x54F SWAP2 SWAP1 PUSH2 0x10E4 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND CALLER EQ PUSH2 0x593 JUMPI PUSH1 0x40 MLOAD PUSH1 0xE5 PUSH1 0x2 EXP PUSH3 0x461BCD MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x58A SWAP1 PUSH2 0x10C4 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH2 0x59C DUP2 PUSH2 0x602 JUMP JUMPDEST ISZERO PUSH2 0x5BC JUMPI PUSH1 0x40 MLOAD PUSH1 0xE5 PUSH1 0x2 EXP PUSH3 0x461BCD MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x58A SWAP1 PUSH2 0x10B4 JUMP JUMPDEST PUSH2 0x5C6 DUP3 DUP3 PUSH2 0xACA JUMP JUMPDEST POP POP JUMP JUMPDEST DUP1 PUSH2 0x5D5 CALLER DUP3 PUSH2 0x8A4 JUMP JUMPDEST ISZERO ISZERO PUSH2 0x5E0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x5FC DUP5 DUP5 DUP5 PUSH1 0x20 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE POP PUSH2 0x7CF JUMP JUMPDEST POP POP POP POP JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND ISZERO ISZERO SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP1 ISZERO ISZERO PUSH2 0x643 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND ISZERO ISZERO PUSH2 0x660 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP2 JUMP JUMPDEST PUSH1 0x2 DUP1 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 PUSH1 0x1F PUSH1 0x0 NOT PUSH2 0x100 PUSH1 0x1 DUP8 AND ISZERO MUL ADD SWAP1 SWAP5 AND DUP6 SWAP1 DIV SWAP4 DUP5 ADD DUP2 SWAP1 DIV DUP2 MUL DUP3 ADD DUP2 ADD SWAP1 SWAP3 MSTORE DUP3 DUP2 MSTORE PUSH1 0x60 SWAP4 SWAP1 SWAP3 SWAP1 SWAP2 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0x393 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x368 JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x393 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND CALLER EQ PUSH2 0x716 JUMPI PUSH1 0x40 MLOAD PUSH1 0xE5 PUSH1 0x2 EXP PUSH3 0x461BCD MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x58A SWAP1 PUSH2 0x10C4 JUMP JUMPDEST PUSH2 0x71F DUP2 PUSH2 0x602 JUMP JUMPDEST ISZERO ISZERO PUSH2 0x740 JUMPI PUSH1 0x40 MLOAD PUSH1 0xE5 PUSH1 0x2 EXP PUSH3 0x461BCD MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x58A SWAP1 PUSH2 0x10D4 JUMP JUMPDEST PUSH2 0x5C6 DUP3 DUP3 PUSH2 0xB2D JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND CALLER EQ ISZERO PUSH2 0x760 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP8 AND DUP1 DUP6 MSTORE SWAP3 MSTORE SWAP2 DUP3 SWAP1 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT AND DUP6 ISZERO ISZERO OR SWAP1 SSTORE SWAP1 MLOAD SWAP1 SWAP2 SWAP1 PUSH32 0x17307EAB39AB6107E8899845AD3D59BD9653F200F220920489CA2B5937696C31 SWAP1 PUSH2 0x7C3 SWAP1 DUP6 SWAP1 PUSH2 0x1095 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP JUMP JUMPDEST DUP2 PUSH2 0x7DA CALLER DUP3 PUSH2 0x8A4 JUMP JUMPDEST ISZERO ISZERO PUSH2 0x7E5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x7F0 DUP6 DUP6 DUP6 PUSH2 0x4AE JUMP JUMPDEST PUSH2 0x7FC DUP6 DUP6 DUP6 DUP6 PUSH2 0xB85 JUMP JUMPDEST ISZERO ISZERO PUSH2 0x807 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP POP POP POP POP JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP2 DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 SWAP1 SWAP5 AND DUP3 MSTORE SWAP2 SWAP1 SWAP2 MSTORE KECCAK256 SLOAD PUSH1 0xFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND CALLER EQ PUSH2 0x869 JUMPI PUSH1 0x40 MLOAD PUSH1 0xE5 PUSH1 0x2 EXP PUSH3 0x461BCD MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x58A SWAP1 PUSH2 0x10C4 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP2 AND ISZERO PUSH2 0x8A1 JUMPI PUSH1 0x0 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND OR SWAP1 SSTORE JUMPDEST POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0x8B0 DUP4 PUSH2 0x61F JUMP JUMPDEST SWAP1 POP DUP1 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP5 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ DUP1 PUSH2 0x8EB JUMPI POP DUP4 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH2 0x8E0 DUP5 PUSH2 0x39D JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ JUMPDEST DUP1 PUSH2 0x8FB JUMPI POP PUSH2 0x8FB DUP2 DUP6 PUSH2 0x80E JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH2 0x916 DUP3 PUSH2 0x61F JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ PUSH2 0x929 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND ISZERO PUSH2 0x5C6 JUMPI PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND SWAP1 SSTORE MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP5 AND SWAP1 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP1 PUSH2 0x7C3 SWAP1 DUP6 SWAP1 PUSH2 0x10E4 JUMP JUMPDEST DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH2 0x9BC DUP3 PUSH2 0x61F JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ PUSH2 0x9CF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x9F3 SWAP1 PUSH1 0x1 PUSH2 0xC8B JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 SWAP1 SWAP5 SSTORE SWAP2 DUP2 MSTORE PUSH1 0x3 SWAP1 SWAP2 MSTORE KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND ISZERO PUSH2 0xA5B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP8 AND SWAP1 DUP2 OR SWAP1 SWAP2 SSTORE DUP4 MSTORE PUSH1 0x5 SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD PUSH2 0xAAA SWAP1 PUSH1 0x1 PUSH2 0xC9D JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SWAP2 SWAP1 SWAP2 SSTORE POP JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND ISZERO ISZERO PUSH2 0xADF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xAE9 DUP3 DUP3 PUSH2 0xA39 JUMP JUMPDEST DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x0 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP4 PUSH1 0x40 MLOAD PUSH2 0x7C3 SWAP2 SWAP1 PUSH2 0x10E4 JUMP JUMPDEST PUSH2 0xB37 DUP3 DUP3 PUSH2 0x903 JUMP JUMPDEST PUSH2 0xB41 DUP3 DUP3 PUSH2 0x9A9 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP3 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP4 PUSH1 0x40 MLOAD PUSH2 0x7C3 SWAP2 SWAP1 PUSH2 0x10E4 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0xB91 DUP6 PUSH2 0xCB3 JUMP JUMPDEST ISZERO ISZERO PUSH2 0xBA0 JUMPI PUSH1 0x1 SWAP2 POP PUSH2 0xC82 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0xF0B9E5BA00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP7 AND SWAP1 PUSH4 0xF0B9E5BA SWAP1 PUSH2 0xBE9 SWAP1 DUP10 SWAP1 DUP9 SWAP1 DUP9 SWAP1 PUSH1 0x4 ADD PUSH2 0x105F JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0xC03 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0xC17 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 0xC3B SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0xEBD JUMP JUMPDEST PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT DUP2 AND PUSH32 0xF0B9E5BA00000000000000000000000000000000000000000000000000000000 EQ SWAP3 POP SWAP1 POP JUMPDEST POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 GT ISZERO PUSH2 0xC97 JUMPI INVALID JUMPDEST POP SWAP1 SUB SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 ADD DUP4 DUP2 LT ISZERO PUSH2 0xCAC JUMPI INVALID JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x0 SWAP1 EXTCODESIZE GT SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xCAC DUP3 CALLDATALOAD PUSH2 0x1145 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xCAC DUP3 CALLDATALOAD PUSH2 0x1151 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xCAC DUP3 MLOAD PUSH2 0x1159 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xCF0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xD03 PUSH2 0xCFE DUP3 PUSH2 0x1119 JUMP JUMPDEST PUSH2 0x10F2 JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH2 0xD1F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xD2A DUP4 DUP3 DUP5 PUSH2 0x117B JUMP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0xCAC DUP3 CALLDATALOAD PUSH2 0x1156 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xD51 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x8FB DUP5 DUP5 PUSH2 0xCBB JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0xD70 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xD7C DUP6 DUP6 PUSH2 0xCBB JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0xD8D DUP6 DUP3 DUP7 ADD PUSH2 0xCBB JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0xDAC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xDB8 DUP7 DUP7 PUSH2 0xCBB JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0xDC9 DUP7 DUP3 DUP8 ADD PUSH2 0xCBB JUMP JUMPDEST SWAP3 POP POP PUSH1 0x40 PUSH2 0xDDA DUP7 DUP3 DUP8 ADD PUSH2 0xD33 JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 POP SWAP3 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x80 DUP6 DUP8 SUB SLT ISZERO PUSH2 0xDFA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xE06 DUP8 DUP8 PUSH2 0xCBB JUMP JUMPDEST SWAP5 POP POP PUSH1 0x20 PUSH2 0xE17 DUP8 DUP3 DUP9 ADD PUSH2 0xCBB JUMP JUMPDEST SWAP4 POP POP PUSH1 0x40 PUSH2 0xE28 DUP8 DUP3 DUP9 ADD PUSH2 0xD33 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x60 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xE45 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xE51 DUP8 DUP3 DUP9 ADD PUSH2 0xCDF JUMP JUMPDEST SWAP2 POP POP SWAP3 SWAP6 SWAP2 SWAP5 POP SWAP3 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0xE70 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xE7C DUP6 DUP6 PUSH2 0xCBB JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0xD8D DUP6 DUP3 DUP7 ADD PUSH2 0xCC7 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0xEA0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xEAC DUP6 DUP6 PUSH2 0xCBB JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0xD8D DUP6 DUP3 DUP7 ADD PUSH2 0xD33 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xECF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x8FB DUP5 DUP5 PUSH2 0xCD3 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xEED JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x8FB DUP5 DUP5 PUSH2 0xD33 JUMP JUMPDEST PUSH2 0xF02 DUP2 PUSH2 0x1145 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH2 0xF02 DUP2 PUSH2 0x1151 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xF1C DUP3 PUSH2 0x1141 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH2 0xF30 DUP2 PUSH1 0x20 DUP7 ADD PUSH1 0x20 DUP7 ADD PUSH2 0x1187 JUMP JUMPDEST PUSH2 0xF39 DUP2 PUSH2 0x11B3 JUMP JUMPDEST SWAP1 SWAP4 ADD PUSH1 0x20 ADD SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x22 DUP2 MSTORE PUSH32 0x546F6B656E207769746820746F6B656E496420616C7265616479206578697374 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x732E000000000000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x33 DUP2 MSTORE PUSH32 0x4F6E6C7920636F6E7472616374206F776E657220697320616C6C6F7765642074 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x6F2063616C6C2074686973206D6574686F642E00000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x22 DUP2 MSTORE PUSH32 0x546F6B656E207769746820746F6B656E496420646F6573206E6F742065786973 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x742E000000000000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH2 0xF02 DUP2 PUSH2 0x1156 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x643 DUP3 DUP5 PUSH2 0xEF9 JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0x106D DUP3 DUP7 PUSH2 0xEF9 JUMP JUMPDEST PUSH2 0x107A PUSH1 0x20 DUP4 ADD DUP6 PUSH2 0x1048 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x40 DUP4 ADD MSTORE PUSH2 0x108C DUP2 DUP5 PUSH2 0xF11 JUMP JUMPDEST SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x643 DUP3 DUP5 PUSH2 0xF08 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xCAC DUP2 DUP5 PUSH2 0xF11 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x643 DUP2 PUSH2 0xF46 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x643 DUP2 PUSH2 0xF9C JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x643 DUP2 PUSH2 0xFF2 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x643 DUP3 DUP5 PUSH2 0x1048 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0x1111 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x1130 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH1 0x1F NOT AND ADD SWAP1 JUMP JUMPDEST MLOAD SWAP1 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP1 JUMP JUMPDEST ISZERO ISZERO SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND SWAP1 JUMP JUMPDEST DUP3 DUP2 DUP4 CALLDATACOPY POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x11A2 JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x118A JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0x5FC JUMPI POP POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x1F ADD PUSH1 0x1F NOT AND SWAP1 JUMP STOP LOG2 PUSH6 0x627A7A723058 KECCAK256 0xf STATICCALL 0xaa 0xd0 SWAP5 0xe9 0xaf 0x24 0xe4 0x2b 0xef 0xc SWAP10 0xc0 ADDRESS GAS INVALID PUSH7 0xDC20DF0F57455A 0xd6 PUSH31 0x1A0741D7C76C6578706572696D656E74616CF5003700000000000000000000 ", + "sourceMap": "734:1237:0:-;;;950:118;8:9:-1;5:2;;;30:1;27;20:12;5:2;950:118:0;;;;;;;;;;;;;;;;;;;;;;;;363:5:5;:18;;-1:-1:-1;;;;;;363:18:5;371:10;363:18;;;2885:13:1;;1048:4:0;;1054:6;;2885:13:1;;363:18:5;;2885:13:1;;;;;:::i;:::-;-1:-1:-1;2908:17:1;;;;:7;;:17;;;;;:::i;:::-;;2788:144;;950:118:0;;734:1237;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;734:1237:0;;;-1:-1:-1;734:1237:0;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::o;6:444:-1:-;;112:4;100:17;;96:27;-1:-1;86:2;;137:1;134;127:12;86:2;167:6;161:13;189:65;204:49;246:6;204:49;;;189:65;;;180:74;;274:6;267:5;260:21;310:4;302:6;298:17;343:4;336:5;332:16;378:3;369:6;364:3;360:16;357:25;354:2;;;395:1;392;385:12;354:2;405:39;437:6;432:3;427;405:39;;;79:371;;;;;;;;458:597;;;610:2;598:9;589:7;585:23;581:32;578:2;;;626:1;623;616:12;578:2;661:24;;-1:-1;;;;;694:30;;691:2;;;737:1;734;727:12;691:2;757:74;823:7;814:6;803:9;799:22;757:74;;;747:84;;640:197;889:2;878:9;874:18;868:25;-1:-1;;;;;905:6;902:30;899:2;;;945:1;942;935:12;899:2;965:74;1031:7;1022:6;1011:9;1007:22;965:74;;;955:84;;847:198;572:483;;;;;;1062:256;1124:2;1118:9;1150:17;;;-1:-1;;;;;1210:34;;1246:22;;;1207:62;1204:2;;;1282:1;1279;1272:12;1204:2;1298;1291:22;1102:216;;-1:-1;1102:216;1325:259;;-1:-1;;;;;1461:6;1458:30;1455:2;;;1501:1;1498;1491:12;1455:2;-1:-1;1574:4;1545;1522:17;;;;-1:-1;;1518:33;1564:15;;1392:192;1592:268;1657:1;1664:101;1678:6;1675:1;1672:13;1664:101;;;1745:11;;;1739:18;1726:11;;;1719:39;1700:2;1693:10;1664:101;;;1780:6;1777:1;1774:13;1771:2;;;1845:1;1836:6;1831:3;1827:16;1820:27;1771:2;1641:219;;;;;;734:1237:0;;;;;;" }, "deployedBytecode": { "linkReferences": {}, - "object": "0x6080604052600436106100da5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde0381146100df578063081812fc1461010a578063095ea7b31461013757806323b872dd1461015957806340c10f191461017957806342842e0e146101995780634f558e79146101b95780636352211e146101e657806370a08231146102065780638da5cb5b1461023357806395d89b4114610248578063a22cb4651461025d578063b88d4fde1461027d578063e985e9c51461029d578063f2fde38b146102bd575b600080fd5b3480156100eb57600080fd5b506100f46102dd565b6040516101019190610fae565b60405180910390f35b34801561011657600080fd5b5061012a610125366004610e3c565b610372565b6040516101019190610f5c565b34801561014357600080fd5b50610157610152366004610dee565b61038d565b005b34801561016557600080fd5b50610157610174366004610cf8565b610483565b34801561018557600080fd5b50610157610194366004610dee565b610532565b3480156101a557600080fd5b506101576101b4366004610cf8565b6105cd565b3480156101c557600080fd5b506101d96101d4366004610e3c565b610605565b6040516101019190610fa0565b3480156101f257600080fd5b5061012a610201366004610e3c565b610622565b34801561021257600080fd5b50610226610221366004610ca0565b61064c565b6040516101019190610fdf565b34801561023f57600080fd5b5061012a61067f565b34801561025457600080fd5b506100f461068e565b34801561026957600080fd5b50610157610278366004610dbe565b6106ec565b34801561028957600080fd5b50610157610298366004610d45565b610771565b3480156102a957600080fd5b506101d96102b8366004610cbe565b6107b0565b3480156102c957600080fd5b506101576102d8366004610ca0565b6107de565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156103685780601f1061033d57610100808354040283529160200191610368565b820191906000526020600020905b81548152906001019060200180831161034b57829003601f168201915b5050505050905090565b600090815260046020526040902054600160a060020a031690565b600061039882610622565b9050600160a060020a0383811690821614156103b357600080fd5b33600160a060020a03821614806103cf57506103cf81336107b0565b15156103da57600080fd5b60006103e583610372565b600160a060020a03161415806104035750600160a060020a03831615155b1561047e5760008281526004602052604090819020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a038681169182179092559151908316907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610475908690610fdf565b60405180910390a35b505050565b8061048e338261085d565b151561049957600080fd5b600160a060020a03841615156104ae57600080fd5b600160a060020a03831615156104c357600080fd5b6104cd84836108bc565b6104d78483610962565b6104e183836109f2565b82600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516105249190610fdf565b60405180910390a350505050565b600054600160a060020a0316331461057f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161057690610fcf565b60405180910390fd5b61058881610605565b156105bf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161057690610fbf565b6105c98282610a83565b5050565b806105d8338261085d565b15156105e357600080fd5b6105ff8484846020604051908101604052806000815250610771565b50505050565b600090815260036020526040902054600160a060020a0316151590565b600081815260036020526040812054600160a060020a031680151561064657600080fd5b92915050565b6000600160a060020a038216151561066357600080fd5b50600160a060020a031660009081526005602052604090205490565b600054600160a060020a031681565b60028054604080516020601f60001961010060018716150201909416859004938401819004810282018101909252828152606093909290918301828280156103685780601f1061033d57610100808354040283529160200191610368565b600160a060020a03821633141561070257600080fd5b336000818152600660209081526040808320600160a060020a038716808552925291829020805460ff191685151517905590519091907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3190610765908590610fa0565b60405180910390a35050565b8161077c338261085d565b151561078757600080fd5b610792858585610483565b61079e85858585610ae6565b15156107a957600080fd5b5050505050565b600160a060020a03918216600090815260066020908152604080832093909416825291909152205460ff1690565b600054600160a060020a03163314610822576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161057690610fcf565b600160a060020a0381161561085a576000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b50565b60008061086983610622565b905080600160a060020a031684600160a060020a031614806108a4575083600160a060020a031661089984610372565b600160a060020a0316145b806108b457506108b481856107b0565b949350505050565b81600160a060020a03166108cf82610622565b600160a060020a0316146108e257600080fd5b600081815260046020526040902054600160a060020a0316156105c957600081815260046020526040808220805473ffffffffffffffffffffffffffffffffffffffff1916905551600160a060020a038416907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610765908590610fdf565b81600160a060020a031661097582610622565b600160a060020a03161461098857600080fd5b600160a060020a0382166000908152600560205260409020546109ac906001610bec565b600160a060020a03909216600090815260056020908152604080832094909455918152600390915220805473ffffffffffffffffffffffffffffffffffffffff19169055565b600081815260036020526040902054600160a060020a031615610a1457600080fd5b6000818152600360209081526040808320805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03871690811790915583526005909152902054610a63906001610bfe565b600160a060020a0390921660009081526005602052604090209190915550565b600160a060020a0382161515610a9857600080fd5b610aa282826109f2565b81600160a060020a03166000600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516107659190610fdf565b600080610af285610c14565b1515610b015760019150610be3565b6040517ff0b9e5ba000000000000000000000000000000000000000000000000000000008152600160a060020a0386169063f0b9e5ba90610b4a90899088908890600401610f6a565b602060405180830381600087803b158015610b6457600080fd5b505af1158015610b78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b9c9190810190610e1e565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1981167ff0b9e5ba0000000000000000000000000000000000000000000000000000000014925090505b50949350505050565b600082821115610bf857fe5b50900390565b600082820183811015610c0d57fe5b9392505050565b6000903b1190565b6000610c0d8235611040565b6000610c0d823561104c565b6000610c0d8251611054565b6000601f82018313610c5157600080fd5b8135610c64610c5f82611014565b610fed565b91508082526020830160208301858383011115610c8057600080fd5b610c8b838284611076565b50505092915050565b6000610c0d8235611051565b600060208284031215610cb257600080fd5b60006108b48484610c1c565b60008060408385031215610cd157600080fd5b6000610cdd8585610c1c565b9250506020610cee85828601610c1c565b9150509250929050565b600080600060608486031215610d0d57600080fd5b6000610d198686610c1c565b9350506020610d2a86828701610c1c565b9250506040610d3b86828701610c94565b9150509250925092565b60008060008060808587031215610d5b57600080fd5b6000610d678787610c1c565b9450506020610d7887828801610c1c565b9350506040610d8987828801610c94565b925050606085013567ffffffffffffffff811115610da657600080fd5b610db287828801610c40565b91505092959194509250565b60008060408385031215610dd157600080fd5b6000610ddd8585610c1c565b9250506020610cee85828601610c28565b60008060408385031215610e0157600080fd5b6000610e0d8585610c1c565b9250506020610cee85828601610c94565b600060208284031215610e3057600080fd5b60006108b48484610c34565b600060208284031215610e4e57600080fd5b60006108b48484610c94565b610e6381611040565b82525050565b610e638161104c565b6000610e7d8261103c565b808452610e91816020860160208601611082565b610e9a816110ae565b9093016020019392505050565b602281527f546f6b656e207769746820746f6b656e496420616c726561647920657869737460208201527f732e000000000000000000000000000000000000000000000000000000000000604082015260600190565b603381527f4f6e6c7920636f6e7472616374206f776e657220697320616c6c6f776564207460208201527f6f2063616c6c2074686973206d6574686f642e00000000000000000000000000604082015260600190565b610e6381611051565b602081016106468284610e5a565b60608101610f788286610e5a565b610f856020830185610f53565b8181036040830152610f978184610e72565b95945050505050565b602081016106468284610e69565b60208082528101610c0d8184610e72565b6020808252810161064681610ea7565b6020808252810161064681610efd565b602081016106468284610f53565b60405181810167ffffffffffffffff8111828210171561100c57600080fd5b604052919050565b600067ffffffffffffffff82111561102b57600080fd5b506020601f91909101601f19160190565b5190565b600160a060020a031690565b151590565b90565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690565b82818337506000910152565b60005b8381101561109d578181015183820152602001611085565b838111156105ff5750506000910152565b601f01601f1916905600a265627a7a72305820dca99ed080138e68d1f26208ce796f211cfc01d8c3da8021f8ee95e54f4b4d2d6c6578706572696d656e74616cf50037", - "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0xDA JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x6FDDE03 DUP2 EQ PUSH2 0xDF JUMPI DUP1 PUSH4 0x81812FC EQ PUSH2 0x10A JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x137 JUMPI DUP1 PUSH4 0x23B872DD EQ PUSH2 0x159 JUMPI DUP1 PUSH4 0x40C10F19 EQ PUSH2 0x179 JUMPI DUP1 PUSH4 0x42842E0E EQ PUSH2 0x199 JUMPI DUP1 PUSH4 0x4F558E79 EQ PUSH2 0x1B9 JUMPI DUP1 PUSH4 0x6352211E EQ PUSH2 0x1E6 JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x206 JUMPI DUP1 PUSH4 0x8DA5CB5B EQ PUSH2 0x233 JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x248 JUMPI DUP1 PUSH4 0xA22CB465 EQ PUSH2 0x25D JUMPI DUP1 PUSH4 0xB88D4FDE EQ PUSH2 0x27D JUMPI DUP1 PUSH4 0xE985E9C5 EQ PUSH2 0x29D JUMPI DUP1 PUSH4 0xF2FDE38B EQ PUSH2 0x2BD JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xEB JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xF4 PUSH2 0x2DD JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x101 SWAP2 SWAP1 PUSH2 0xFAE JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x116 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x12A PUSH2 0x125 CALLDATASIZE PUSH1 0x4 PUSH2 0xE3C JUMP JUMPDEST PUSH2 0x372 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x101 SWAP2 SWAP1 PUSH2 0xF5C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x143 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x157 PUSH2 0x152 CALLDATASIZE PUSH1 0x4 PUSH2 0xDEE JUMP JUMPDEST PUSH2 0x38D JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x165 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x157 PUSH2 0x174 CALLDATASIZE PUSH1 0x4 PUSH2 0xCF8 JUMP JUMPDEST PUSH2 0x483 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x185 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x157 PUSH2 0x194 CALLDATASIZE PUSH1 0x4 PUSH2 0xDEE JUMP JUMPDEST PUSH2 0x532 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1A5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x157 PUSH2 0x1B4 CALLDATASIZE PUSH1 0x4 PUSH2 0xCF8 JUMP JUMPDEST PUSH2 0x5CD JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1C5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1D9 PUSH2 0x1D4 CALLDATASIZE PUSH1 0x4 PUSH2 0xE3C JUMP JUMPDEST PUSH2 0x605 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x101 SWAP2 SWAP1 PUSH2 0xFA0 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1F2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x12A PUSH2 0x201 CALLDATASIZE PUSH1 0x4 PUSH2 0xE3C JUMP JUMPDEST PUSH2 0x622 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x212 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x226 PUSH2 0x221 CALLDATASIZE PUSH1 0x4 PUSH2 0xCA0 JUMP JUMPDEST PUSH2 0x64C JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x101 SWAP2 SWAP1 PUSH2 0xFDF JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x23F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x12A PUSH2 0x67F JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x254 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xF4 PUSH2 0x68E JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x269 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x157 PUSH2 0x278 CALLDATASIZE PUSH1 0x4 PUSH2 0xDBE JUMP JUMPDEST PUSH2 0x6EC JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x289 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x157 PUSH2 0x298 CALLDATASIZE PUSH1 0x4 PUSH2 0xD45 JUMP JUMPDEST PUSH2 0x771 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2A9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1D9 PUSH2 0x2B8 CALLDATASIZE PUSH1 0x4 PUSH2 0xCBE JUMP JUMPDEST PUSH2 0x7B0 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2C9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x157 PUSH2 0x2D8 CALLDATASIZE PUSH1 0x4 PUSH2 0xCA0 JUMP JUMPDEST PUSH2 0x7DE JUMP JUMPDEST PUSH1 0x1 DUP1 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 PUSH1 0x1F PUSH1 0x2 PUSH1 0x0 NOT PUSH2 0x100 DUP8 DUP10 AND ISZERO MUL ADD SWAP1 SWAP6 AND SWAP5 SWAP1 SWAP5 DIV SWAP4 DUP5 ADD DUP2 SWAP1 DIV DUP2 MUL DUP3 ADD DUP2 ADD SWAP1 SWAP3 MSTORE DUP3 DUP2 MSTORE PUSH1 0x60 SWAP4 SWAP1 SWAP3 SWAP1 SWAP2 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0x368 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x33D JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x368 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 0x34B JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP POP POP SWAP1 POP SWAP1 JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x398 DUP3 PUSH2 0x622 JUMP JUMPDEST SWAP1 POP PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 DUP2 AND SWAP1 DUP3 AND EQ ISZERO PUSH2 0x3B3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST CALLER PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND EQ DUP1 PUSH2 0x3CF JUMPI POP PUSH2 0x3CF DUP2 CALLER PUSH2 0x7B0 JUMP JUMPDEST ISZERO ISZERO PUSH2 0x3DA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x3E5 DUP4 PUSH2 0x372 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ ISZERO DUP1 PUSH2 0x403 JUMPI POP PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND ISZERO ISZERO JUMPDEST ISZERO PUSH2 0x47E JUMPI PUSH1 0x0 DUP3 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP7 DUP2 AND SWAP2 DUP3 OR SWAP1 SWAP3 SSTORE SWAP2 MLOAD SWAP1 DUP4 AND SWAP1 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP1 PUSH2 0x475 SWAP1 DUP7 SWAP1 PUSH2 0xFDF JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 JUMPDEST POP POP POP JUMP JUMPDEST DUP1 PUSH2 0x48E CALLER DUP3 PUSH2 0x85D JUMP JUMPDEST ISZERO ISZERO PUSH2 0x499 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP5 AND ISZERO ISZERO PUSH2 0x4AE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND ISZERO ISZERO PUSH2 0x4C3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4CD DUP5 DUP4 PUSH2 0x8BC JUMP JUMPDEST PUSH2 0x4D7 DUP5 DUP4 PUSH2 0x962 JUMP JUMPDEST PUSH2 0x4E1 DUP4 DUP4 PUSH2 0x9F2 JUMP JUMPDEST DUP3 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP5 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP5 PUSH1 0x40 MLOAD PUSH2 0x524 SWAP2 SWAP1 PUSH2 0xFDF JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND CALLER EQ PUSH2 0x57F JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x576 SWAP1 PUSH2 0xFCF JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH2 0x588 DUP2 PUSH2 0x605 JUMP JUMPDEST ISZERO PUSH2 0x5BF JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x576 SWAP1 PUSH2 0xFBF JUMP JUMPDEST PUSH2 0x5C9 DUP3 DUP3 PUSH2 0xA83 JUMP JUMPDEST POP POP JUMP JUMPDEST DUP1 PUSH2 0x5D8 CALLER DUP3 PUSH2 0x85D JUMP JUMPDEST ISZERO ISZERO PUSH2 0x5E3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x5FF DUP5 DUP5 DUP5 PUSH1 0x20 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE POP PUSH2 0x771 JUMP JUMPDEST POP POP POP POP JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND ISZERO ISZERO SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP1 ISZERO ISZERO PUSH2 0x646 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND ISZERO ISZERO PUSH2 0x663 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP2 JUMP JUMPDEST PUSH1 0x2 DUP1 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 PUSH1 0x1F PUSH1 0x0 NOT PUSH2 0x100 PUSH1 0x1 DUP8 AND ISZERO MUL ADD SWAP1 SWAP5 AND DUP6 SWAP1 DIV SWAP4 DUP5 ADD DUP2 SWAP1 DIV DUP2 MUL DUP3 ADD DUP2 ADD SWAP1 SWAP3 MSTORE DUP3 DUP2 MSTORE PUSH1 0x60 SWAP4 SWAP1 SWAP3 SWAP1 SWAP2 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0x368 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x33D JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x368 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND CALLER EQ ISZERO PUSH2 0x702 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP8 AND DUP1 DUP6 MSTORE SWAP3 MSTORE SWAP2 DUP3 SWAP1 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT AND DUP6 ISZERO ISZERO OR SWAP1 SSTORE SWAP1 MLOAD SWAP1 SWAP2 SWAP1 PUSH32 0x17307EAB39AB6107E8899845AD3D59BD9653F200F220920489CA2B5937696C31 SWAP1 PUSH2 0x765 SWAP1 DUP6 SWAP1 PUSH2 0xFA0 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP JUMP JUMPDEST DUP2 PUSH2 0x77C CALLER DUP3 PUSH2 0x85D JUMP JUMPDEST ISZERO ISZERO PUSH2 0x787 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x792 DUP6 DUP6 DUP6 PUSH2 0x483 JUMP JUMPDEST PUSH2 0x79E DUP6 DUP6 DUP6 DUP6 PUSH2 0xAE6 JUMP JUMPDEST ISZERO ISZERO PUSH2 0x7A9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP POP POP POP POP JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP2 DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 SWAP1 SWAP5 AND DUP3 MSTORE SWAP2 SWAP1 SWAP2 MSTORE KECCAK256 SLOAD PUSH1 0xFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND CALLER EQ PUSH2 0x822 JUMPI PUSH1 0x40 MLOAD PUSH32 0x8C379A000000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x576 SWAP1 PUSH2 0xFCF JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP2 AND ISZERO PUSH2 0x85A JUMPI PUSH1 0x0 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND OR SWAP1 SSTORE JUMPDEST POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0x869 DUP4 PUSH2 0x622 JUMP JUMPDEST SWAP1 POP DUP1 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP5 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ DUP1 PUSH2 0x8A4 JUMPI POP DUP4 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH2 0x899 DUP5 PUSH2 0x372 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ JUMPDEST DUP1 PUSH2 0x8B4 JUMPI POP PUSH2 0x8B4 DUP2 DUP6 PUSH2 0x7B0 JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH2 0x8CF DUP3 PUSH2 0x622 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ PUSH2 0x8E2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND ISZERO PUSH2 0x5C9 JUMPI PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND SWAP1 SSTORE MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP5 AND SWAP1 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP1 PUSH2 0x765 SWAP1 DUP6 SWAP1 PUSH2 0xFDF JUMP JUMPDEST DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH2 0x975 DUP3 PUSH2 0x622 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ PUSH2 0x988 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x9AC SWAP1 PUSH1 0x1 PUSH2 0xBEC JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 SWAP1 SWAP5 SSTORE SWAP2 DUP2 MSTORE PUSH1 0x3 SWAP1 SWAP2 MSTORE KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND ISZERO PUSH2 0xA14 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP8 AND SWAP1 DUP2 OR SWAP1 SWAP2 SSTORE DUP4 MSTORE PUSH1 0x5 SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD PUSH2 0xA63 SWAP1 PUSH1 0x1 PUSH2 0xBFE JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SWAP2 SWAP1 SWAP2 SSTORE POP JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND ISZERO ISZERO PUSH2 0xA98 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xAA2 DUP3 DUP3 PUSH2 0x9F2 JUMP JUMPDEST DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x0 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP4 PUSH1 0x40 MLOAD PUSH2 0x765 SWAP2 SWAP1 PUSH2 0xFDF JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0xAF2 DUP6 PUSH2 0xC14 JUMP JUMPDEST ISZERO ISZERO PUSH2 0xB01 JUMPI PUSH1 0x1 SWAP2 POP PUSH2 0xBE3 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0xF0B9E5BA00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP7 AND SWAP1 PUSH4 0xF0B9E5BA SWAP1 PUSH2 0xB4A SWAP1 DUP10 SWAP1 DUP9 SWAP1 DUP9 SWAP1 PUSH1 0x4 ADD PUSH2 0xF6A JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0xB64 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0xB78 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 0xB9C SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0xE1E JUMP JUMPDEST PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT DUP2 AND PUSH32 0xF0B9E5BA00000000000000000000000000000000000000000000000000000000 EQ SWAP3 POP SWAP1 POP JUMPDEST POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 GT ISZERO PUSH2 0xBF8 JUMPI INVALID JUMPDEST POP SWAP1 SUB SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 ADD DUP4 DUP2 LT ISZERO PUSH2 0xC0D JUMPI INVALID JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x0 SWAP1 EXTCODESIZE GT SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC0D DUP3 CALLDATALOAD PUSH2 0x1040 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC0D DUP3 CALLDATALOAD PUSH2 0x104C JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC0D DUP3 MLOAD PUSH2 0x1054 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xC51 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xC64 PUSH2 0xC5F DUP3 PUSH2 0x1014 JUMP JUMPDEST PUSH2 0xFED JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH2 0xC80 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xC8B DUP4 DUP3 DUP5 PUSH2 0x1076 JUMP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0xC0D DUP3 CALLDATALOAD PUSH2 0x1051 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xCB2 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x8B4 DUP5 DUP5 PUSH2 0xC1C JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0xCD1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xCDD DUP6 DUP6 PUSH2 0xC1C JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0xCEE DUP6 DUP3 DUP7 ADD PUSH2 0xC1C JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0xD0D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xD19 DUP7 DUP7 PUSH2 0xC1C JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0xD2A DUP7 DUP3 DUP8 ADD PUSH2 0xC1C JUMP JUMPDEST SWAP3 POP POP PUSH1 0x40 PUSH2 0xD3B DUP7 DUP3 DUP8 ADD PUSH2 0xC94 JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 POP SWAP3 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x80 DUP6 DUP8 SUB SLT ISZERO PUSH2 0xD5B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xD67 DUP8 DUP8 PUSH2 0xC1C JUMP JUMPDEST SWAP5 POP POP PUSH1 0x20 PUSH2 0xD78 DUP8 DUP3 DUP9 ADD PUSH2 0xC1C JUMP JUMPDEST SWAP4 POP POP PUSH1 0x40 PUSH2 0xD89 DUP8 DUP3 DUP9 ADD PUSH2 0xC94 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x60 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xDA6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xDB2 DUP8 DUP3 DUP9 ADD PUSH2 0xC40 JUMP JUMPDEST SWAP2 POP POP SWAP3 SWAP6 SWAP2 SWAP5 POP SWAP3 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0xDD1 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xDDD DUP6 DUP6 PUSH2 0xC1C JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0xCEE DUP6 DUP3 DUP7 ADD PUSH2 0xC28 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0xE01 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xE0D DUP6 DUP6 PUSH2 0xC1C JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0xCEE DUP6 DUP3 DUP7 ADD PUSH2 0xC94 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xE30 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x8B4 DUP5 DUP5 PUSH2 0xC34 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xE4E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x8B4 DUP5 DUP5 PUSH2 0xC94 JUMP JUMPDEST PUSH2 0xE63 DUP2 PUSH2 0x1040 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH2 0xE63 DUP2 PUSH2 0x104C JUMP JUMPDEST PUSH1 0x0 PUSH2 0xE7D DUP3 PUSH2 0x103C JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH2 0xE91 DUP2 PUSH1 0x20 DUP7 ADD PUSH1 0x20 DUP7 ADD PUSH2 0x1082 JUMP JUMPDEST PUSH2 0xE9A DUP2 PUSH2 0x10AE JUMP JUMPDEST SWAP1 SWAP4 ADD PUSH1 0x20 ADD SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x22 DUP2 MSTORE PUSH32 0x546F6B656E207769746820746F6B656E496420616C7265616479206578697374 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x732E000000000000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x33 DUP2 MSTORE PUSH32 0x4F6E6C7920636F6E7472616374206F776E657220697320616C6C6F7765642074 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x6F2063616C6C2074686973206D6574686F642E00000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH2 0xE63 DUP2 PUSH2 0x1051 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x646 DUP3 DUP5 PUSH2 0xE5A JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0xF78 DUP3 DUP7 PUSH2 0xE5A JUMP JUMPDEST PUSH2 0xF85 PUSH1 0x20 DUP4 ADD DUP6 PUSH2 0xF53 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x40 DUP4 ADD MSTORE PUSH2 0xF97 DUP2 DUP5 PUSH2 0xE72 JUMP JUMPDEST SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x646 DUP3 DUP5 PUSH2 0xE69 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xC0D DUP2 DUP5 PUSH2 0xE72 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x646 DUP2 PUSH2 0xEA7 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x646 DUP2 PUSH2 0xEFD JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x646 DUP3 DUP5 PUSH2 0xF53 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0x100C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x102B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH1 0x1F NOT AND ADD SWAP1 JUMP JUMPDEST MLOAD SWAP1 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP1 JUMP JUMPDEST ISZERO ISZERO SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND SWAP1 JUMP JUMPDEST DUP3 DUP2 DUP4 CALLDATACOPY POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x109D JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x1085 JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0x5FF JUMPI POP POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x1F ADD PUSH1 0x1F NOT AND SWAP1 JUMP STOP LOG2 PUSH6 0x627A7A723058 KECCAK256 0xdc 0xa9 SWAP15 0xd0 DUP1 SGT DUP15 PUSH9 0xD1F26208CE796F211C 0xfc ADD 0xd8 0xc3 0xda DUP1 0x21 0xf8 0xee SWAP6 0xe5 0x4f 0x4b 0x4d 0x2d PUSH13 0x6578706572696D656E74616CF5 STOP CALLDATACOPY ", - "sourceMap": "734:822:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3033:102:1;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3033:102:1;;;;;;;;;;;;;;;;;;;;5586:145;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;5586:145:1;;;;;;;;;;;;;;;;;4948:401;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4948:401:1;;;;;;;;;;;7190:362;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;7190:362:1;;;;;;;;;1332:222:0;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1332:222:0;;;;;;;;;8183:254:1;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;8183:254:1;;;;;;;;;4339:178;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4339:178:1;;;;;;;;;;;;;;;;;3947:206;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3947:206:1;;;;;;;;;3546:180;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3546:180:1;;;;;;;;;;;;;;;;;292:20:5;;8:9:-1;5:2;;;30:1;27;20:12;5:2;292:20:5;;;;3240:106:1;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3240:106:1;;;;6025:231;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;6025:231:1;;;;;;;;;9139:339;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;9139:339:1;;;;;;;;;6574:176;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;6574:176:1;;;;;;;;;566:167:5;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;566:167:5;;;;;;;;;3033:102:1;3123:5;3116:12;;;;;;;;-1:-1:-1;;3116:12:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3094:6;;3116:12;;3123:5;;3116:12;;3123:5;3116:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3033:102;:::o;5586:145::-;5670:7;5700:24;;;:14;:24;;;;;;-1:-1:-1;;;;;5700:24:1;;5586:145::o;4948:401::-;5025:13;5041:17;5049:8;5041:7;:17::i;:::-;5025:33;-1:-1:-1;;;;;;5076:12:1;;;;;;;;5068:21;;;;;;5107:10;-1:-1:-1;;;;;5107:19:1;;;;:58;;;5130:35;5147:5;5154:10;5130:16;:35::i;:::-;5099:67;;;;;;;;5214:1;5181:21;5193:8;5181:11;:21::i;:::-;-1:-1:-1;;;;;5181:35:1;;;:56;;;-1:-1:-1;;;;;;5220:17:1;;;;5181:56;5177:166;;;5253:24;;;;:14;:24;;;;;;;:30;;-1:-1:-1;;5253:30:1;-1:-1:-1;;;;;5253:30:1;;;;;;;;;5302;;;;;;;;;;5253:24;;5302:30;;;;;;;;;;5177:166;4948:401;;;:::o;7190:362::-;7293:8;2724:39;2742:10;2754:8;2724:17;:39::i;:::-;2716:48;;;;;;;;-1:-1:-1;;;;;7325:19:1;;;;7317:28;;;;;;-1:-1:-1;;;;;7363:17:1;;;;7355:26;;;;;;7392:30;7406:5;7413:8;7392:13;:30::i;:::-;7432:32;7448:5;7455:8;7432:15;:32::i;:::-;7474:25;7485:3;7490:8;7474:10;:25::i;:::-;7531:3;-1:-1:-1;;;;;7515:30:1;7524:5;-1:-1:-1;;;;;7515:30:1;;7536:8;7515:30;;;;;;;;;;;;;;;7190:362;;;;:::o;1332:222:0:-;460:5:5;;-1:-1:-1;;;;;460:5:5;446:10;:19;425:117;;;;;;;;;;;;;;;;;;;;;;1444:15:0;1451:7;1444:6;:15::i;:::-;1443:16;1422:97;;;;;;;;;;;;;;1529:18;1535:2;1539:7;1529:5;:18::i;:::-;1332:222;;:::o;8183:254:1:-;8315:8;2724:39;2742:10;2754:8;2724:17;:39::i;:::-;2716:48;;;;;;;;8388:42;8405:5;8412:3;8417:8;8388:42;;;;;;;;;;;;;:16;:42::i;:::-;8183:254;;;;:::o;4339:178::-;4418:4;4454:20;;;:10;:20;;;;;;-1:-1:-1;;;;;4454:20:1;4491:19;;;4339:178::o;3947:206::-;4027:7;4066:20;;;:10;:20;;;;;;-1:-1:-1;;;;;4066:20:1;4104:19;;;4096:28;;;;;;4141:5;3947:206;-1:-1:-1;;3947:206:1:o;3546:180::-;3626:7;-1:-1:-1;;;;;3657:20:1;;;;3649:29;;;;;;-1:-1:-1;;;;;;3695:24:1;;;;;:16;:24;;;;;;;3546:180::o;292:20:5:-;;;-1:-1:-1;;;;;292:20:5;;:::o;3240:106:1:-;3332:7;3325:14;;;;;;;-1:-1:-1;;3325:14:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3303:6;;3325:14;;3332:7;;3325:14;;3332:7;3325:14;;;;;;;;;;;;;;;;;;;;;;;;6025:231;-1:-1:-1;;;;;6118:17:1;;6125:10;6118:17;;6110:26;;;;;;6164:10;6146:29;;;;:17;:29;;;;;;;;-1:-1:-1;;;;;6146:34:1;;;;;;;;;;;:46;;-1:-1:-1;;6146:46:1;;;;;;;6207:42;;6146:34;;6164:10;6207:42;;;;6146:46;;6207:42;;;;;;;;;;6025:231;;:::o;9139:339::-;9292:8;2724:39;2742:10;2754:8;2724:17;:39::i;:::-;2716:48;;;;;;;;9316:34;9329:5;9336:3;9341:8;9316:12;:34::i;:::-;9417:53;9442:5;9449:3;9454:8;9464:5;9417:24;:53::i;:::-;9409:62;;;;;;;;9139:339;;;;;:::o;6574:176::-;-1:-1:-1;;;;;6707:25:1;;;6680:4;6707:25;;;:17;:25;;;;;;;;:36;;;;;;;;;;;;;;;6574:176::o;566:167:5:-;460:5;;-1:-1:-1;;;;;460:5:5;446:10;:19;425:117;;;;;;;;;;;;;;-1:-1:-1;;;;;662:22:5;;;658:69;;700:5;:16;;-1:-1:-1;;700:16:5;-1:-1:-1;;;;;700:16:5;;;;;658:69;566:167;:::o;9835:278:1:-;9945:4;9965:13;9981:17;9989:8;9981:7;:17::i;:::-;9965:33;;10027:5;-1:-1:-1;;;;;10015:17:1;:8;-1:-1:-1;;;;;10015:17:1;;:54;;;;10061:8;-1:-1:-1;;;;;10036:33:1;:21;10048:8;10036:11;:21::i;:::-;-1:-1:-1;;;;;10036:33:1;;10015:54;:91;;;;10073:33;10090:5;10097:8;10073:16;:33::i;:::-;10008:98;9835:278;-1:-1:-1;;;;9835:278:1:o;11260:303::-;11377:6;-1:-1:-1;;;;;11356:27:1;:17;11364:8;11356:7;:17::i;:::-;-1:-1:-1;;;;;11356:27:1;;11348:36;;;;;;11434:1;11398:24;;;:14;:24;;;;;;-1:-1:-1;;;;;11398:24:1;:38;11394:163;;11487:1;11452:24;;;:14;:24;;;;;;:37;;-1:-1:-1;;11452:37:1;;;11508:38;-1:-1:-1;;;;;11508:38:1;;;;;;;11467:8;;11508:38;;12356:245;12474:5;-1:-1:-1;;;;;12453:26:1;:17;12461:8;12453:7;:17::i;:::-;-1:-1:-1;;;;;12453:26:1;;12445:35;;;;;;-1:-1:-1;;;;;12524:23:1;;;;;;:16;:23;;;;;;12516:35;;12549:1;12516:7;:35::i;:::-;-1:-1:-1;;;;;12490:23:1;;;;;;;:16;:23;;;;;;;;:61;;;;12561:20;;;:10;:20;;;;:33;;-1:-1:-1;;12561:33:1;;;12356:245::o;11834:235::-;11956:1;11924:20;;;:10;:20;;;;;;-1:-1:-1;;;;;11924:20:1;:34;11916:43;;;;;;11969:20;;;;:10;:20;;;;;;;;:26;;-1:-1:-1;;11969:26:1;-1:-1:-1;;;;;11969:26:1;;;;;;;;12037:21;;:16;:21;;;;;;12029:33;;-1:-1:-1;12029:7:1;:33::i;:::-;-1:-1:-1;;;;;12005:21:1;;;;;;;:16;:21;;;;;:57;;;;-1:-1:-1;11834:235:1:o;10376:195::-;-1:-1:-1;;;;;10461:17:1;;;;10453:26;;;;;;10489:25;10500:3;10505:8;10489:10;:25::i;:::-;10550:3;-1:-1:-1;;;;;10529:35:1;10546:1;-1:-1:-1;;;;;10529:35:1;;10555:8;10529:35;;;;;;;13124:375;13284:4;13371:13;13309:15;13320:3;13309:10;:15::i;:::-;13308:16;13304:58;;;13347:4;13340:11;;;;13304:58;13387:61;;;;;-1:-1:-1;;;;;13387:37:1;;;;;:61;;13425:5;;13432:8;;13442:5;;13387:61;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;13387:61:1;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;13387:61:1;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;13387:61:1;;;;;;;;;-1:-1:-1;;13466:25:1;;13476:15;13466:25;;-1:-1:-1;13371:77:1;-1:-1:-1;13124:375:1;;;;;;;;:::o;418:146:6:-;498:7;528:6;;;;521:14;;;;-1:-1:-1;552:5:6;;;418:146::o;570:166::-;650:7;682:5;;;704:6;;;;697:14;;;;728:1;570:166;-1:-1:-1;;;570:166:6:o;13505:634:1:-;13586:4;14037:17;;14124:8;;13505:634::o;5:118:-1:-;;72:46;110:6;97:20;72:46;;130:112;;194:43;229:6;216:20;194:43;;249:120;;326:38;356:6;350:13;326:38;;377:440;;471:4;459:17;;455:27;-1:-1;445:2;;496:1;493;486:12;445:2;533:6;520:20;555:64;570:48;611:6;570:48;;;555:64;;;546:73;;639:6;632:5;625:21;675:4;667:6;663:17;708:4;701:5;697:16;743:3;734:6;729:3;725:16;722:25;719:2;;;760:1;757;750:12;719:2;770:41;804:6;799:3;794;770:41;;;438:379;;;;;;;;825:118;;892:46;930:6;917:20;892:46;;950:241;;1054:2;1042:9;1033:7;1029:23;1025:32;1022:2;;;1070:1;1067;1060:12;1022:2;1105:1;1122:53;1167:7;1147:9;1122:53;;1198:366;;;1319:2;1307:9;1298:7;1294:23;1290:32;1287:2;;;1335:1;1332;1325:12;1287:2;1370:1;1387:53;1432:7;1412:9;1387:53;;;1377:63;;1349:97;1477:2;1495:53;1540:7;1531:6;1520:9;1516:22;1495:53;;;1485:63;;1456:98;1281:283;;;;;;1571:491;;;;1709:2;1697:9;1688:7;1684:23;1680:32;1677:2;;;1725:1;1722;1715:12;1677:2;1760:1;1777:53;1822:7;1802:9;1777:53;;;1767:63;;1739:97;1867:2;1885:53;1930:7;1921:6;1910:9;1906:22;1885:53;;;1875:63;;1846:98;1975:2;1993:53;2038:7;2029:6;2018:9;2014:22;1993:53;;;1983:63;;1954:98;1671:391;;;;;;2069:721;;;;;2233:3;2221:9;2212:7;2208:23;2204:33;2201:2;;;2250:1;2247;2240:12;2201:2;2285:1;2302:53;2347:7;2327:9;2302:53;;;2292:63;;2264:97;2392:2;2410:53;2455:7;2446:6;2435:9;2431:22;2410:53;;;2400:63;;2371:98;2500:2;2518:53;2563:7;2554:6;2543:9;2539:22;2518:53;;;2508:63;;2479:98;2636:2;2625:9;2621:18;2608:32;2660:18;2652:6;2649:30;2646:2;;;2692:1;2689;2682:12;2646:2;2712:62;2766:7;2757:6;2746:9;2742:22;2712:62;;;2702:72;;2587:193;2195:595;;;;;;;;2797:360;;;2915:2;2903:9;2894:7;2890:23;2886:32;2883:2;;;2931:1;2928;2921:12;2883:2;2966:1;2983:53;3028:7;3008:9;2983:53;;;2973:63;;2945:97;3073:2;3091:50;3133:7;3124:6;3113:9;3109:22;3091:50;;3164:366;;;3285:2;3273:9;3264:7;3260:23;3256:32;3253:2;;;3301:1;3298;3291:12;3253:2;3336:1;3353:53;3398:7;3378:9;3353:53;;;3343:63;;3315:97;3443:2;3461:53;3506:7;3497:6;3486:9;3482:22;3461:53;;3537:261;;3651:2;3639:9;3630:7;3626:23;3622:32;3619:2;;;3667:1;3664;3657:12;3619:2;3702:1;3719:63;3774:7;3754:9;3719:63;;3805:241;;3909:2;3897:9;3888:7;3884:23;3880:32;3877:2;;;3925:1;3922;3915:12;3877:2;3960:1;3977:53;4022:7;4002:9;3977:53;;4053:110;4126:31;4151:5;4126:31;;;4121:3;4114:44;4108:55;;;4170:101;4237:28;4259:5;4237:28;;4278:297;;4378:38;4410:5;4378:38;;;4433:6;4428:3;4421:19;4445:63;4501:6;4494:4;4489:3;4485:14;4478:4;4471:5;4467:16;4445:63;;;4540:29;4562:6;4540:29;;;4520:50;;;4533:4;4520:50;;4358:217;-1:-1;;;4358:217;4890:397;5045:2;5033:15;;5082:66;5077:2;5068:12;;5061:88;5183:66;5178:2;5169:12;;5162:88;5278:2;5269:12;;5026:261;5296:397;5451:2;5439:15;;5488:66;5483:2;5474:12;;5467:88;5589:66;5584:2;5575:12;;5568:88;5684:2;5675:12;;5432:261;5701:110;5774:31;5799:5;5774:31;;5818:193;5926:2;5911:18;;5940:61;5915:9;5974:6;5940:61;;6018:479;6200:2;6185:18;;6214:61;6189:9;6248:6;6214:61;;;6286:62;6344:2;6333:9;6329:18;6320:6;6286:62;;;6396:9;6390:4;6386:20;6381:2;6370:9;6366:18;6359:48;6421:66;6482:4;6473:6;6421:66;;;6413:74;6171:326;-1:-1;;;;;6171:326;6504:181;6606:2;6591:18;;6620:55;6595:9;6648:6;6620:55;;6692:281;6820:2;6834:47;;;6805:18;;6895:68;6805:18;6949:6;6895:68;;6980:387;7161:2;7175:47;;;7146:18;;7236:121;7146:18;7236:121;;7374:387;7555:2;7569:47;;;7540:18;;7630:121;7540:18;7630:121;;7768:193;7876:2;7861:18;;7890:61;7865:9;7924:6;7890:61;;7968:256;8030:2;8024:9;8056:17;;;8131:18;8116:34;;8152:22;;;8113:62;8110:2;;;8188:1;8185;8178:12;8110:2;8204;8197:22;8008:216;;-1:-1;8008:216;8231:258;;8374:18;8366:6;8363:30;8360:2;;;8406:1;8403;8396:12;8360:2;-1:-1;8479:4;8450;8427:17;;;;-1:-1;;8423:33;8469:15;;8297:192;8496:91;8570:12;;8554:33;8693:128;-1:-1;;;;;8762:54;;8745:76;8828:92;8901:13;8894:21;;8877:43;8927:79;8996:5;8979:27;9247:151;-1:-1;;9315:78;;9298:100;9492:145;9573:6;9568:3;9563;9550:30;-1:-1;9629:1;9611:16;;9604:27;9543:94;9646:268;9711:1;9718:101;9732:6;9729:1;9726:13;9718:101;;;9799:11;;;9793:18;9780:11;;;9773:39;9754:2;9747:10;9718:101;;;9834:6;9831:1;9828:13;9825:2;;;-1:-1;;9899:1;9881:16;;9874:27;9695:219;9922:97;10010:2;9990:14;-1:-1;;9986:28;;9970:49" + "object": "0x6080604052600436106100e55763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde0381146100ea578063081812fc14610115578063095ea7b31461014257806323b872dd1461016457806340c10f191461018457806342842e0e146101a45780634f558e79146101c45780636352211e146101f157806370a08231146102115780638da5cb5b1461023e57806395d89b41146102535780639dc29fac14610268578063a22cb46514610288578063b88d4fde146102a8578063e985e9c5146102c8578063f2fde38b146102e8575b600080fd5b3480156100f657600080fd5b506100ff610308565b60405161010c91906110a3565b60405180910390f35b34801561012157600080fd5b50610135610130366004610edb565b61039d565b60405161010c9190611051565b34801561014e57600080fd5b5061016261015d366004610e8d565b6103b8565b005b34801561017057600080fd5b5061016261017f366004610d97565b6104ae565b34801561019057600080fd5b5061016261019f366004610e8d565b61055d565b3480156101b057600080fd5b506101626101bf366004610d97565b6105ca565b3480156101d057600080fd5b506101e46101df366004610edb565b610602565b60405161010c9190611095565b3480156101fd57600080fd5b5061013561020c366004610edb565b61061f565b34801561021d57600080fd5b5061023161022c366004610d3f565b610649565b60405161010c91906110e4565b34801561024a57600080fd5b5061013561067c565b34801561025f57600080fd5b506100ff61068b565b34801561027457600080fd5b50610162610283366004610e8d565b6106e9565b34801561029457600080fd5b506101626102a3366004610e5d565b61074a565b3480156102b457600080fd5b506101626102c3366004610de4565b6107cf565b3480156102d457600080fd5b506101e46102e3366004610d5d565b61080e565b3480156102f457600080fd5b50610162610303366004610d3f565b61083c565b60018054604080516020601f600260001961010087891615020190951694909404938401819004810282018101909252828152606093909290918301828280156103935780601f1061036857610100808354040283529160200191610393565b820191906000526020600020905b81548152906001019060200180831161037657829003601f168201915b5050505050905090565b600090815260046020526040902054600160a060020a031690565b60006103c38261061f565b9050600160a060020a0383811690821614156103de57600080fd5b33600160a060020a03821614806103fa57506103fa813361080e565b151561040557600080fd5b60006104108361039d565b600160a060020a031614158061042e5750600160a060020a03831615155b156104a95760008281526004602052604090819020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a038681169182179092559151908316907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906104a09086906110e4565b60405180910390a35b505050565b806104b933826108a4565b15156104c457600080fd5b600160a060020a03841615156104d957600080fd5b600160a060020a03831615156104ee57600080fd5b6104f88483610903565b61050284836109a9565b61050c8383610a39565b82600160a060020a031684600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161054f91906110e4565b60405180910390a350505050565b600054600160a060020a031633146105935760405160e560020a62461bcd02815260040161058a906110c4565b60405180910390fd5b61059c81610602565b156105bc5760405160e560020a62461bcd02815260040161058a906110b4565b6105c68282610aca565b5050565b806105d533826108a4565b15156105e057600080fd5b6105fc84848460206040519081016040528060008152506107cf565b50505050565b600090815260036020526040902054600160a060020a0316151590565b600081815260036020526040812054600160a060020a031680151561064357600080fd5b92915050565b6000600160a060020a038216151561066057600080fd5b50600160a060020a031660009081526005602052604090205490565b600054600160a060020a031681565b60028054604080516020601f60001961010060018716150201909416859004938401819004810282018101909252828152606093909290918301828280156103935780601f1061036857610100808354040283529160200191610393565b600054600160a060020a031633146107165760405160e560020a62461bcd02815260040161058a906110c4565b61071f81610602565b15156107405760405160e560020a62461bcd02815260040161058a906110d4565b6105c68282610b2d565b600160a060020a03821633141561076057600080fd5b336000818152600660209081526040808320600160a060020a038716808552925291829020805460ff191685151517905590519091907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31906107c3908590611095565b60405180910390a35050565b816107da33826108a4565b15156107e557600080fd5b6107f08585856104ae565b6107fc85858585610b85565b151561080757600080fd5b5050505050565b600160a060020a03918216600090815260066020908152604080832093909416825291909152205460ff1690565b600054600160a060020a031633146108695760405160e560020a62461bcd02815260040161058a906110c4565b600160a060020a038116156108a1576000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0383161790555b50565b6000806108b08361061f565b905080600160a060020a031684600160a060020a031614806108eb575083600160a060020a03166108e08461039d565b600160a060020a0316145b806108fb57506108fb818561080e565b949350505050565b81600160a060020a03166109168261061f565b600160a060020a03161461092957600080fd5b600081815260046020526040902054600160a060020a0316156105c657600081815260046020526040808220805473ffffffffffffffffffffffffffffffffffffffff1916905551600160a060020a038416907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906107c39085906110e4565b81600160a060020a03166109bc8261061f565b600160a060020a0316146109cf57600080fd5b600160a060020a0382166000908152600560205260409020546109f3906001610c8b565b600160a060020a03909216600090815260056020908152604080832094909455918152600390915220805473ffffffffffffffffffffffffffffffffffffffff19169055565b600081815260036020526040902054600160a060020a031615610a5b57600080fd5b6000818152600360209081526040808320805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03871690811790915583526005909152902054610aaa906001610c9d565b600160a060020a0390921660009081526005602052604090209190915550565b600160a060020a0382161515610adf57600080fd5b610ae98282610a39565b81600160a060020a03166000600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516107c391906110e4565b610b378282610903565b610b4182826109a9565b6000600160a060020a031682600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516107c391906110e4565b600080610b9185610cb3565b1515610ba05760019150610c82565b6040517ff0b9e5ba000000000000000000000000000000000000000000000000000000008152600160a060020a0386169063f0b9e5ba90610be99089908890889060040161105f565b602060405180830381600087803b158015610c0357600080fd5b505af1158015610c17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c3b9190810190610ebd565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1981167ff0b9e5ba0000000000000000000000000000000000000000000000000000000014925090505b50949350505050565b600082821115610c9757fe5b50900390565b600082820183811015610cac57fe5b9392505050565b6000903b1190565b6000610cac8235611145565b6000610cac8235611151565b6000610cac8251611159565b6000601f82018313610cf057600080fd5b8135610d03610cfe82611119565b6110f2565b91508082526020830160208301858383011115610d1f57600080fd5b610d2a83828461117b565b50505092915050565b6000610cac8235611156565b600060208284031215610d5157600080fd5b60006108fb8484610cbb565b60008060408385031215610d7057600080fd5b6000610d7c8585610cbb565b9250506020610d8d85828601610cbb565b9150509250929050565b600080600060608486031215610dac57600080fd5b6000610db88686610cbb565b9350506020610dc986828701610cbb565b9250506040610dda86828701610d33565b9150509250925092565b60008060008060808587031215610dfa57600080fd5b6000610e068787610cbb565b9450506020610e1787828801610cbb565b9350506040610e2887828801610d33565b925050606085013567ffffffffffffffff811115610e4557600080fd5b610e5187828801610cdf565b91505092959194509250565b60008060408385031215610e7057600080fd5b6000610e7c8585610cbb565b9250506020610d8d85828601610cc7565b60008060408385031215610ea057600080fd5b6000610eac8585610cbb565b9250506020610d8d85828601610d33565b600060208284031215610ecf57600080fd5b60006108fb8484610cd3565b600060208284031215610eed57600080fd5b60006108fb8484610d33565b610f0281611145565b82525050565b610f0281611151565b6000610f1c82611141565b808452610f30816020860160208601611187565b610f39816111b3565b9093016020019392505050565b602281527f546f6b656e207769746820746f6b656e496420616c726561647920657869737460208201527f732e000000000000000000000000000000000000000000000000000000000000604082015260600190565b603381527f4f6e6c7920636f6e7472616374206f776e657220697320616c6c6f776564207460208201527f6f2063616c6c2074686973206d6574686f642e00000000000000000000000000604082015260600190565b602281527f546f6b656e207769746820746f6b656e496420646f6573206e6f74206578697360208201527f742e000000000000000000000000000000000000000000000000000000000000604082015260600190565b610f0281611156565b602081016106438284610ef9565b6060810161106d8286610ef9565b61107a6020830185611048565b818103604083015261108c8184610f11565b95945050505050565b602081016106438284610f08565b60208082528101610cac8184610f11565b6020808252810161064381610f46565b6020808252810161064381610f9c565b6020808252810161064381610ff2565b602081016106438284611048565b60405181810167ffffffffffffffff8111828210171561111157600080fd5b604052919050565b600067ffffffffffffffff82111561113057600080fd5b506020601f91909101601f19160190565b5190565b600160a060020a031690565b151590565b90565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690565b82818337506000910152565b60005b838110156111a257818101518382015260200161118a565b838111156105fc5750506000910152565b601f01601f1916905600a265627a7a723058200ffaaad094e9af24e42bef0c99c0305afe66dc20df0f57455ad67e1a0741d7c76c6578706572696d656e74616cf50037", + "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0xE5 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x6FDDE03 DUP2 EQ PUSH2 0xEA JUMPI DUP1 PUSH4 0x81812FC EQ PUSH2 0x115 JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x142 JUMPI DUP1 PUSH4 0x23B872DD EQ PUSH2 0x164 JUMPI DUP1 PUSH4 0x40C10F19 EQ PUSH2 0x184 JUMPI DUP1 PUSH4 0x42842E0E EQ PUSH2 0x1A4 JUMPI DUP1 PUSH4 0x4F558E79 EQ PUSH2 0x1C4 JUMPI DUP1 PUSH4 0x6352211E EQ PUSH2 0x1F1 JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x211 JUMPI DUP1 PUSH4 0x8DA5CB5B EQ PUSH2 0x23E JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x253 JUMPI DUP1 PUSH4 0x9DC29FAC EQ PUSH2 0x268 JUMPI DUP1 PUSH4 0xA22CB465 EQ PUSH2 0x288 JUMPI DUP1 PUSH4 0xB88D4FDE EQ PUSH2 0x2A8 JUMPI DUP1 PUSH4 0xE985E9C5 EQ PUSH2 0x2C8 JUMPI DUP1 PUSH4 0xF2FDE38B EQ PUSH2 0x2E8 JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xF6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xFF PUSH2 0x308 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x10C SWAP2 SWAP1 PUSH2 0x10A3 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x121 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x135 PUSH2 0x130 CALLDATASIZE PUSH1 0x4 PUSH2 0xEDB JUMP JUMPDEST PUSH2 0x39D JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x10C SWAP2 SWAP1 PUSH2 0x1051 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x14E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x15D CALLDATASIZE PUSH1 0x4 PUSH2 0xE8D JUMP JUMPDEST PUSH2 0x3B8 JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x170 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x17F CALLDATASIZE PUSH1 0x4 PUSH2 0xD97 JUMP JUMPDEST PUSH2 0x4AE JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x190 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x19F CALLDATASIZE PUSH1 0x4 PUSH2 0xE8D JUMP JUMPDEST PUSH2 0x55D JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1B0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x1BF CALLDATASIZE PUSH1 0x4 PUSH2 0xD97 JUMP JUMPDEST PUSH2 0x5CA JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1D0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1E4 PUSH2 0x1DF CALLDATASIZE PUSH1 0x4 PUSH2 0xEDB JUMP JUMPDEST PUSH2 0x602 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x10C SWAP2 SWAP1 PUSH2 0x1095 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1FD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x135 PUSH2 0x20C CALLDATASIZE PUSH1 0x4 PUSH2 0xEDB JUMP JUMPDEST PUSH2 0x61F JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x21D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x231 PUSH2 0x22C CALLDATASIZE PUSH1 0x4 PUSH2 0xD3F JUMP JUMPDEST PUSH2 0x649 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH2 0x10C SWAP2 SWAP1 PUSH2 0x10E4 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x24A JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x135 PUSH2 0x67C JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x25F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xFF PUSH2 0x68B JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x274 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x283 CALLDATASIZE PUSH1 0x4 PUSH2 0xE8D JUMP JUMPDEST PUSH2 0x6E9 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x294 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x2A3 CALLDATASIZE PUSH1 0x4 PUSH2 0xE5D JUMP JUMPDEST PUSH2 0x74A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2B4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x2C3 CALLDATASIZE PUSH1 0x4 PUSH2 0xDE4 JUMP JUMPDEST PUSH2 0x7CF JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2D4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1E4 PUSH2 0x2E3 CALLDATASIZE PUSH1 0x4 PUSH2 0xD5D JUMP JUMPDEST PUSH2 0x80E JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x2F4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x162 PUSH2 0x303 CALLDATASIZE PUSH1 0x4 PUSH2 0xD3F JUMP JUMPDEST PUSH2 0x83C JUMP JUMPDEST PUSH1 0x1 DUP1 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 PUSH1 0x1F PUSH1 0x2 PUSH1 0x0 NOT PUSH2 0x100 DUP8 DUP10 AND ISZERO MUL ADD SWAP1 SWAP6 AND SWAP5 SWAP1 SWAP5 DIV SWAP4 DUP5 ADD DUP2 SWAP1 DIV DUP2 MUL DUP3 ADD DUP2 ADD SWAP1 SWAP3 MSTORE DUP3 DUP2 MSTORE PUSH1 0x60 SWAP4 SWAP1 SWAP3 SWAP1 SWAP2 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0x393 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x368 JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x393 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 0x376 JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP POP POP SWAP1 POP SWAP1 JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0x3C3 DUP3 PUSH2 0x61F JUMP JUMPDEST SWAP1 POP PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 DUP2 AND SWAP1 DUP3 AND EQ ISZERO PUSH2 0x3DE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST CALLER PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND EQ DUP1 PUSH2 0x3FA JUMPI POP PUSH2 0x3FA DUP2 CALLER PUSH2 0x80E JUMP JUMPDEST ISZERO ISZERO PUSH2 0x405 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x410 DUP4 PUSH2 0x39D JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ ISZERO DUP1 PUSH2 0x42E JUMPI POP PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND ISZERO ISZERO JUMPDEST ISZERO PUSH2 0x4A9 JUMPI PUSH1 0x0 DUP3 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP7 DUP2 AND SWAP2 DUP3 OR SWAP1 SWAP3 SSTORE SWAP2 MLOAD SWAP1 DUP4 AND SWAP1 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP1 PUSH2 0x4A0 SWAP1 DUP7 SWAP1 PUSH2 0x10E4 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 JUMPDEST POP POP POP JUMP JUMPDEST DUP1 PUSH2 0x4B9 CALLER DUP3 PUSH2 0x8A4 JUMP JUMPDEST ISZERO ISZERO PUSH2 0x4C4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP5 AND ISZERO ISZERO PUSH2 0x4D9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND ISZERO ISZERO PUSH2 0x4EE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x4F8 DUP5 DUP4 PUSH2 0x903 JUMP JUMPDEST PUSH2 0x502 DUP5 DUP4 PUSH2 0x9A9 JUMP JUMPDEST PUSH2 0x50C DUP4 DUP4 PUSH2 0xA39 JUMP JUMPDEST DUP3 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP5 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP5 PUSH1 0x40 MLOAD PUSH2 0x54F SWAP2 SWAP1 PUSH2 0x10E4 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND CALLER EQ PUSH2 0x593 JUMPI PUSH1 0x40 MLOAD PUSH1 0xE5 PUSH1 0x2 EXP PUSH3 0x461BCD MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x58A SWAP1 PUSH2 0x10C4 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 REVERT JUMPDEST PUSH2 0x59C DUP2 PUSH2 0x602 JUMP JUMPDEST ISZERO PUSH2 0x5BC JUMPI PUSH1 0x40 MLOAD PUSH1 0xE5 PUSH1 0x2 EXP PUSH3 0x461BCD MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x58A SWAP1 PUSH2 0x10B4 JUMP JUMPDEST PUSH2 0x5C6 DUP3 DUP3 PUSH2 0xACA JUMP JUMPDEST POP POP JUMP JUMPDEST DUP1 PUSH2 0x5D5 CALLER DUP3 PUSH2 0x8A4 JUMP JUMPDEST ISZERO ISZERO PUSH2 0x5E0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x5FC DUP5 DUP5 DUP5 PUSH1 0x20 PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 PUSH1 0x0 DUP2 MSTORE POP PUSH2 0x7CF JUMP JUMPDEST POP POP POP POP JUMP JUMPDEST PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND ISZERO ISZERO SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP1 ISZERO ISZERO PUSH2 0x643 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND ISZERO ISZERO PUSH2 0x660 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD SWAP1 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP2 JUMP JUMPDEST PUSH1 0x2 DUP1 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 PUSH1 0x1F PUSH1 0x0 NOT PUSH2 0x100 PUSH1 0x1 DUP8 AND ISZERO MUL ADD SWAP1 SWAP5 AND DUP6 SWAP1 DIV SWAP4 DUP5 ADD DUP2 SWAP1 DIV DUP2 MUL DUP3 ADD DUP2 ADD SWAP1 SWAP3 MSTORE DUP3 DUP2 MSTORE PUSH1 0x60 SWAP4 SWAP1 SWAP3 SWAP1 SWAP2 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0x393 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x368 JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x393 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND CALLER EQ PUSH2 0x716 JUMPI PUSH1 0x40 MLOAD PUSH1 0xE5 PUSH1 0x2 EXP PUSH3 0x461BCD MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x58A SWAP1 PUSH2 0x10C4 JUMP JUMPDEST PUSH2 0x71F DUP2 PUSH2 0x602 JUMP JUMPDEST ISZERO ISZERO PUSH2 0x740 JUMPI PUSH1 0x40 MLOAD PUSH1 0xE5 PUSH1 0x2 EXP PUSH3 0x461BCD MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x58A SWAP1 PUSH2 0x10D4 JUMP JUMPDEST PUSH2 0x5C6 DUP3 DUP3 PUSH2 0xB2D JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND CALLER EQ ISZERO PUSH2 0x760 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP8 AND DUP1 DUP6 MSTORE SWAP3 MSTORE SWAP2 DUP3 SWAP1 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT AND DUP6 ISZERO ISZERO OR SWAP1 SSTORE SWAP1 MLOAD SWAP1 SWAP2 SWAP1 PUSH32 0x17307EAB39AB6107E8899845AD3D59BD9653F200F220920489CA2B5937696C31 SWAP1 PUSH2 0x7C3 SWAP1 DUP6 SWAP1 PUSH2 0x1095 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 POP POP JUMP JUMPDEST DUP2 PUSH2 0x7DA CALLER DUP3 PUSH2 0x8A4 JUMP JUMPDEST ISZERO ISZERO PUSH2 0x7E5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x7F0 DUP6 DUP6 DUP6 PUSH2 0x4AE JUMP JUMPDEST PUSH2 0x7FC DUP6 DUP6 DUP6 DUP6 PUSH2 0xB85 JUMP JUMPDEST ISZERO ISZERO PUSH2 0x807 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP POP POP POP POP JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP2 DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x6 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP4 SWAP1 SWAP5 AND DUP3 MSTORE SWAP2 SWAP1 SWAP2 MSTORE KECCAK256 SLOAD PUSH1 0xFF AND SWAP1 JUMP JUMPDEST PUSH1 0x0 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND CALLER EQ PUSH2 0x869 JUMPI PUSH1 0x40 MLOAD PUSH1 0xE5 PUSH1 0x2 EXP PUSH3 0x461BCD MUL DUP2 MSTORE PUSH1 0x4 ADD PUSH2 0x58A SWAP1 PUSH2 0x10C4 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP2 AND ISZERO PUSH2 0x8A1 JUMPI PUSH1 0x0 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND OR SWAP1 SSTORE JUMPDEST POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0x8B0 DUP4 PUSH2 0x61F JUMP JUMPDEST SWAP1 POP DUP1 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP5 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ DUP1 PUSH2 0x8EB JUMPI POP DUP4 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH2 0x8E0 DUP5 PUSH2 0x39D JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ JUMPDEST DUP1 PUSH2 0x8FB JUMPI POP PUSH2 0x8FB DUP2 DUP6 PUSH2 0x80E JUMP JUMPDEST SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH2 0x916 DUP3 PUSH2 0x61F JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ PUSH2 0x929 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND ISZERO PUSH2 0x5C6 JUMPI PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x4 PUSH1 0x20 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND SWAP1 SSTORE MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP5 AND SWAP1 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP1 PUSH2 0x7C3 SWAP1 DUP6 SWAP1 PUSH2 0x10E4 JUMP JUMPDEST DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH2 0x9BC DUP3 PUSH2 0x61F JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ PUSH2 0x9CF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH2 0x9F3 SWAP1 PUSH1 0x1 PUSH2 0xC8B JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 SWAP5 SWAP1 SWAP5 SSTORE SWAP2 DUP2 MSTORE PUSH1 0x3 SWAP1 SWAP2 MSTORE KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND SWAP1 SSTORE JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND ISZERO PUSH2 0xA5B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x3 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 DUP1 SLOAD PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP8 AND SWAP1 DUP2 OR SWAP1 SWAP2 SSTORE DUP4 MSTORE PUSH1 0x5 SWAP1 SWAP2 MSTORE SWAP1 KECCAK256 SLOAD PUSH2 0xAAA SWAP1 PUSH1 0x1 PUSH2 0xC9D JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x5 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SWAP2 SWAP1 SWAP2 SSTORE POP JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND ISZERO ISZERO PUSH2 0xADF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xAE9 DUP3 DUP3 PUSH2 0xA39 JUMP JUMPDEST DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x0 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP4 PUSH1 0x40 MLOAD PUSH2 0x7C3 SWAP2 SWAP1 PUSH2 0x10E4 JUMP JUMPDEST PUSH2 0xB37 DUP3 DUP3 PUSH2 0x903 JUMP JUMPDEST PUSH2 0xB41 DUP3 DUP3 PUSH2 0x9A9 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP3 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP4 PUSH1 0x40 MLOAD PUSH2 0x7C3 SWAP2 SWAP1 PUSH2 0x10E4 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH2 0xB91 DUP6 PUSH2 0xCB3 JUMP JUMPDEST ISZERO ISZERO PUSH2 0xBA0 JUMPI PUSH1 0x1 SWAP2 POP PUSH2 0xC82 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH32 0xF0B9E5BA00000000000000000000000000000000000000000000000000000000 DUP2 MSTORE PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP7 AND SWAP1 PUSH4 0xF0B9E5BA SWAP1 PUSH2 0xBE9 SWAP1 DUP10 SWAP1 DUP9 SWAP1 DUP9 SWAP1 PUSH1 0x4 ADD PUSH2 0x105F JUMP JUMPDEST PUSH1 0x20 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 PUSH1 0x0 DUP8 DUP1 EXTCODESIZE ISZERO DUP1 ISZERO PUSH2 0xC03 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP GAS CALL ISZERO DUP1 ISZERO PUSH2 0xC17 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 0xC3B SWAP2 SWAP1 DUP2 ADD SWAP1 PUSH2 0xEBD JUMP JUMPDEST PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT DUP2 AND PUSH32 0xF0B9E5BA00000000000000000000000000000000000000000000000000000000 EQ SWAP3 POP SWAP1 POP JUMPDEST POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 GT ISZERO PUSH2 0xC97 JUMPI INVALID JUMPDEST POP SWAP1 SUB SWAP1 JUMP JUMPDEST PUSH1 0x0 DUP3 DUP3 ADD DUP4 DUP2 LT ISZERO PUSH2 0xCAC JUMPI INVALID JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x0 SWAP1 EXTCODESIZE GT SWAP1 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xCAC DUP3 CALLDATALOAD PUSH2 0x1145 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xCAC DUP3 CALLDATALOAD PUSH2 0x1151 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xCAC DUP3 MLOAD PUSH2 0x1159 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x1F DUP3 ADD DUP4 SGT PUSH2 0xCF0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 CALLDATALOAD PUSH2 0xD03 PUSH2 0xCFE DUP3 PUSH2 0x1119 JUMP JUMPDEST PUSH2 0x10F2 JUMP JUMPDEST SWAP2 POP DUP1 DUP3 MSTORE PUSH1 0x20 DUP4 ADD PUSH1 0x20 DUP4 ADD DUP6 DUP4 DUP4 ADD GT ISZERO PUSH2 0xD1F JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xD2A DUP4 DUP3 DUP5 PUSH2 0x117B JUMP JUMPDEST POP POP POP SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0xCAC DUP3 CALLDATALOAD PUSH2 0x1156 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xD51 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x8FB DUP5 DUP5 PUSH2 0xCBB JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0xD70 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xD7C DUP6 DUP6 PUSH2 0xCBB JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0xD8D DUP6 DUP3 DUP7 ADD PUSH2 0xCBB JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 PUSH1 0x60 DUP5 DUP7 SUB SLT ISZERO PUSH2 0xDAC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xDB8 DUP7 DUP7 PUSH2 0xCBB JUMP JUMPDEST SWAP4 POP POP PUSH1 0x20 PUSH2 0xDC9 DUP7 DUP3 DUP8 ADD PUSH2 0xCBB JUMP JUMPDEST SWAP3 POP POP PUSH1 0x40 PUSH2 0xDDA DUP7 DUP3 DUP8 ADD PUSH2 0xD33 JUMP JUMPDEST SWAP2 POP POP SWAP3 POP SWAP3 POP SWAP3 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x0 DUP1 PUSH1 0x80 DUP6 DUP8 SUB SLT ISZERO PUSH2 0xDFA JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xE06 DUP8 DUP8 PUSH2 0xCBB JUMP JUMPDEST SWAP5 POP POP PUSH1 0x20 PUSH2 0xE17 DUP8 DUP3 DUP9 ADD PUSH2 0xCBB JUMP JUMPDEST SWAP4 POP POP PUSH1 0x40 PUSH2 0xE28 DUP8 DUP3 DUP9 ADD PUSH2 0xD33 JUMP JUMPDEST SWAP3 POP POP PUSH1 0x60 DUP6 ADD CALLDATALOAD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT ISZERO PUSH2 0xE45 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0xE51 DUP8 DUP3 DUP9 ADD PUSH2 0xCDF JUMP JUMPDEST SWAP2 POP POP SWAP3 SWAP6 SWAP2 SWAP5 POP SWAP3 POP JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0xE70 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xE7C DUP6 DUP6 PUSH2 0xCBB JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0xD8D DUP6 DUP3 DUP7 ADD PUSH2 0xCC7 JUMP JUMPDEST PUSH1 0x0 DUP1 PUSH1 0x40 DUP4 DUP6 SUB SLT ISZERO PUSH2 0xEA0 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0xEAC DUP6 DUP6 PUSH2 0xCBB JUMP JUMPDEST SWAP3 POP POP PUSH1 0x20 PUSH2 0xD8D DUP6 DUP3 DUP7 ADD PUSH2 0xD33 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xECF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x8FB DUP5 DUP5 PUSH2 0xCD3 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP3 DUP5 SUB SLT ISZERO PUSH2 0xEED JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 PUSH2 0x8FB DUP5 DUP5 PUSH2 0xD33 JUMP JUMPDEST PUSH2 0xF02 DUP2 PUSH2 0x1145 JUMP JUMPDEST DUP3 MSTORE POP POP JUMP JUMPDEST PUSH2 0xF02 DUP2 PUSH2 0x1151 JUMP JUMPDEST PUSH1 0x0 PUSH2 0xF1C DUP3 PUSH2 0x1141 JUMP JUMPDEST DUP1 DUP5 MSTORE PUSH2 0xF30 DUP2 PUSH1 0x20 DUP7 ADD PUSH1 0x20 DUP7 ADD PUSH2 0x1187 JUMP JUMPDEST PUSH2 0xF39 DUP2 PUSH2 0x11B3 JUMP JUMPDEST SWAP1 SWAP4 ADD PUSH1 0x20 ADD SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x22 DUP2 MSTORE PUSH32 0x546F6B656E207769746820746F6B656E496420616C7265616479206578697374 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x732E000000000000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x33 DUP2 MSTORE PUSH32 0x4F6E6C7920636F6E7472616374206F776E657220697320616C6C6F7765642074 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x6F2063616C6C2074686973206D6574686F642E00000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH1 0x22 DUP2 MSTORE PUSH32 0x546F6B656E207769746820746F6B656E496420646F6573206E6F742065786973 PUSH1 0x20 DUP3 ADD MSTORE PUSH32 0x742E000000000000000000000000000000000000000000000000000000000000 PUSH1 0x40 DUP3 ADD MSTORE PUSH1 0x60 ADD SWAP1 JUMP JUMPDEST PUSH2 0xF02 DUP2 PUSH2 0x1156 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x643 DUP3 DUP5 PUSH2 0xEF9 JUMP JUMPDEST PUSH1 0x60 DUP2 ADD PUSH2 0x106D DUP3 DUP7 PUSH2 0xEF9 JUMP JUMPDEST PUSH2 0x107A PUSH1 0x20 DUP4 ADD DUP6 PUSH2 0x1048 JUMP JUMPDEST DUP2 DUP2 SUB PUSH1 0x40 DUP4 ADD MSTORE PUSH2 0x108C DUP2 DUP5 PUSH2 0xF11 JUMP JUMPDEST SWAP6 SWAP5 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x643 DUP3 DUP5 PUSH2 0xF08 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0xCAC DUP2 DUP5 PUSH2 0xF11 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x643 DUP2 PUSH2 0xF46 JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x643 DUP2 PUSH2 0xF9C JUMP JUMPDEST PUSH1 0x20 DUP1 DUP3 MSTORE DUP2 ADD PUSH2 0x643 DUP2 PUSH2 0xFF2 JUMP JUMPDEST PUSH1 0x20 DUP2 ADD PUSH2 0x643 DUP3 DUP5 PUSH2 0x1048 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP2 DUP2 ADD PUSH8 0xFFFFFFFFFFFFFFFF DUP2 GT DUP3 DUP3 LT OR ISZERO PUSH2 0x1111 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x40 MSTORE SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH8 0xFFFFFFFFFFFFFFFF DUP3 GT ISZERO PUSH2 0x1130 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x20 PUSH1 0x1F SWAP2 SWAP1 SWAP2 ADD PUSH1 0x1F NOT AND ADD SWAP1 JUMP JUMPDEST MLOAD SWAP1 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP1 JUMP JUMPDEST ISZERO ISZERO SWAP1 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH28 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF NOT AND SWAP1 JUMP JUMPDEST DUP3 DUP2 DUP4 CALLDATACOPY POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x11A2 JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x118A JUMP JUMPDEST DUP4 DUP2 GT ISZERO PUSH2 0x5FC JUMPI POP POP PUSH1 0x0 SWAP2 ADD MSTORE JUMP JUMPDEST PUSH1 0x1F ADD PUSH1 0x1F NOT AND SWAP1 JUMP STOP LOG2 PUSH6 0x627A7A723058 KECCAK256 0xf STATICCALL 0xaa 0xd0 SWAP5 0xe9 0xaf 0x24 0xe4 0x2b 0xef 0xc SWAP10 0xc0 ADDRESS GAS INVALID PUSH7 0xDC20DF0F57455A 0xd6 PUSH31 0x1A0741D7C76C6578706572696D656E74616CF5003700000000000000000000 ", + "sourceMap": "734:1237:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3033:102:1;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3033:102:1;;;;;;;;;;;;;;;;;;;;5586:145;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;5586:145:1;;;;;;;;;;;;;;;;;4948:401;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4948:401:1;;;;;;;;;;;7190:362;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;7190:362:1;;;;;;;;;1332:222:0;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1332:222:0;;;;;;;;;8183:254:1;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;8183:254:1;;;;;;;;;4339:178;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4339:178:1;;;;;;;;;;;;;;;;;3947:206;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3947:206:1;;;;;;;;;3546:180;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3546:180:1;;;;;;;;;;;;;;;;;292:20:5;;8:9:-1;5:2;;;30:1;27;20:12;5:2;292:20:5;;;;3240:106:1;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3240:106:1;;;;1742:227:0;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1742:227:0;;;;;;;;;6025:231:1;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;6025:231:1;;;;;;;;;9139:339;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;9139:339:1;;;;;;;;;6574:176;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;6574:176:1;;;;;;;;;566:167:5;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;566:167:5;;;;;;;;;3033:102:1;3123:5;3116:12;;;;;;;;-1:-1:-1;;3116:12:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3094:6;;3116:12;;3123:5;;3116:12;;3123:5;3116:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3033:102;:::o;5586:145::-;5670:7;5700:24;;;:14;:24;;;;;;-1:-1:-1;;;;;5700:24:1;;5586:145::o;4948:401::-;5025:13;5041:17;5049:8;5041:7;:17::i;:::-;5025:33;-1:-1:-1;;;;;;5076:12:1;;;;;;;;5068:21;;;;;;5107:10;-1:-1:-1;;;;;5107:19:1;;;;:58;;;5130:35;5147:5;5154:10;5130:16;:35::i;:::-;5099:67;;;;;;;;5214:1;5181:21;5193:8;5181:11;:21::i;:::-;-1:-1:-1;;;;;5181:35:1;;;:56;;;-1:-1:-1;;;;;;5220:17:1;;;;5181:56;5177:166;;;5253:24;;;;:14;:24;;;;;;;:30;;-1:-1:-1;;5253:30:1;-1:-1:-1;;;;;5253:30:1;;;;;;;;;5302;;;;;;;;;;5253:24;;5302:30;;;;;;;;;;5177:166;4948:401;;;:::o;7190:362::-;7293:8;2724:39;2742:10;2754:8;2724:17;:39::i;:::-;2716:48;;;;;;;;-1:-1:-1;;;;;7325:19:1;;;;7317:28;;;;;;-1:-1:-1;;;;;7363:17:1;;;;7355:26;;;;;;7392:30;7406:5;7413:8;7392:13;:30::i;:::-;7432:32;7448:5;7455:8;7432:15;:32::i;:::-;7474:25;7485:3;7490:8;7474:10;:25::i;:::-;7531:3;-1:-1:-1;;;;;7515:30:1;7524:5;-1:-1:-1;;;;;7515:30:1;;7536:8;7515:30;;;;;;;;;;;;;;;7190:362;;;;:::o;1332:222:0:-;460:5:5;;-1:-1:-1;;;;;460:5:5;446:10;:19;425:117;;;;-1:-1:-1;;;;;425:117:5;;;;;;;;;;;;;;;;;1444:15:0;1451:7;1444:6;:15::i;:::-;1443:16;1422:97;;;;-1:-1:-1;;;;;1422:97:0;;;;;;;;;1529:18;1535:2;1539:7;1529:5;:18::i;:::-;1332:222;;:::o;8183:254:1:-;8315:8;2724:39;2742:10;2754:8;2724:17;:39::i;:::-;2716:48;;;;;;;;8388:42;8405:5;8412:3;8417:8;8388:42;;;;;;;;;;;;;:16;:42::i;:::-;8183:254;;;;:::o;4339:178::-;4418:4;4454:20;;;:10;:20;;;;;;-1:-1:-1;;;;;4454:20:1;4491:19;;;4339:178::o;3947:206::-;4027:7;4066:20;;;:10;:20;;;;;;-1:-1:-1;;;;;4066:20:1;4104:19;;;4096:28;;;;;;4141:5;3947:206;-1:-1:-1;;3947:206:1:o;3546:180::-;3626:7;-1:-1:-1;;;;;3657:20:1;;;;3649:29;;;;;;-1:-1:-1;;;;;;3695:24:1;;;;;:16;:24;;;;;;;3546:180::o;292:20:5:-;;;-1:-1:-1;;;;;292:20:5;;:::o;3240:106:1:-;3332:7;3325:14;;;;;;;-1:-1:-1;;3325:14:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3303:6;;3325:14;;3332:7;;3325:14;;3332:7;3325:14;;;;;;;;;;;;;;;;;;;;;;;;1742:227:0;460:5:5;;-1:-1:-1;;;;;460:5:5;446:10;:19;425:117;;;;-1:-1:-1;;;;;425:117:5;;;;;;;;;1856:15:0;1863:7;1856:6;:15::i;:::-;1835:96;;;;;;-1:-1:-1;;;;;1835:96:0;;;;;;;;;1941:21;1947:5;1954:7;1941:5;:21::i;6025:231:1:-;-1:-1:-1;;;;;6118:17:1;;6125:10;6118:17;;6110:26;;;;;;6164:10;6146:29;;;;:17;:29;;;;;;;;-1:-1:-1;;;;;6146:34:1;;;;;;;;;;;:46;;-1:-1:-1;;6146:46:1;;;;;;;6207:42;;6146:34;;6164:10;6207:42;;;;6146:46;;6207:42;;;;;;;;;;6025:231;;:::o;9139:339::-;9292:8;2724:39;2742:10;2754:8;2724:17;:39::i;:::-;2716:48;;;;;;;;9316:34;9329:5;9336:3;9341:8;9316:12;:34::i;:::-;9417:53;9442:5;9449:3;9454:8;9464:5;9417:24;:53::i;:::-;9409:62;;;;;;;;9139:339;;;;;:::o;6574:176::-;-1:-1:-1;;;;;6707:25:1;;;6680:4;6707:25;;;:17;:25;;;;;;;;:36;;;;;;;;;;;;;;;6574:176::o;566:167:5:-;460:5;;-1:-1:-1;;;;;460:5:5;446:10;:19;425:117;;;;-1:-1:-1;;;;;425:117:5;;;;;;;;;-1:-1:-1;;;;;662:22:5;;;658:69;;700:5;:16;;-1:-1:-1;;700:16:5;-1:-1:-1;;;;;700:16:5;;;;;658:69;566:167;:::o;9835:278:1:-;9945:4;9965:13;9981:17;9989:8;9981:7;:17::i;:::-;9965:33;;10027:5;-1:-1:-1;;;;;10015:17:1;:8;-1:-1:-1;;;;;10015:17:1;;:54;;;;10061:8;-1:-1:-1;;;;;10036:33:1;:21;10048:8;10036:11;:21::i;:::-;-1:-1:-1;;;;;10036:33:1;;10015:54;:91;;;;10073:33;10090:5;10097:8;10073:16;:33::i;:::-;10008:98;9835:278;-1:-1:-1;;;;9835:278:1:o;11260:303::-;11377:6;-1:-1:-1;;;;;11356:27:1;:17;11364:8;11356:7;:17::i;:::-;-1:-1:-1;;;;;11356:27:1;;11348:36;;;;;;11434:1;11398:24;;;:14;:24;;;;;;-1:-1:-1;;;;;11398:24:1;:38;11394:163;;11487:1;11452:24;;;:14;:24;;;;;;:37;;-1:-1:-1;;11452:37:1;;;11508:38;-1:-1:-1;;;;;11508:38:1;;;;;;;11467:8;;11508:38;;12356:245;12474:5;-1:-1:-1;;;;;12453:26:1;:17;12461:8;12453:7;:17::i;:::-;-1:-1:-1;;;;;12453:26:1;;12445:35;;;;;;-1:-1:-1;;;;;12524:23:1;;;;;;:16;:23;;;;;;12516:35;;12549:1;12516:7;:35::i;:::-;-1:-1:-1;;;;;12490:23:1;;;;;;;:16;:23;;;;;;;;:61;;;;12561:20;;;:10;:20;;;;:33;;-1:-1:-1;;12561:33:1;;;12356:245::o;11834:235::-;11956:1;11924:20;;;:10;:20;;;;;;-1:-1:-1;;;;;11924:20:1;:34;11916:43;;;;;;11969:20;;;;:10;:20;;;;;;;;:26;;-1:-1:-1;;11969:26:1;-1:-1:-1;;;;;11969:26:1;;;;;;;;12037:21;;:16;:21;;;;;;12029:33;;-1:-1:-1;12029:7:1;:33::i;:::-;-1:-1:-1;;;;;12005:21:1;;;;;;;:16;:21;;;;;:57;;;;-1:-1:-1;11834:235:1:o;10376:195::-;-1:-1:-1;;;;;10461:17:1;;;;10453:26;;;;;;10489:25;10500:3;10505:8;10489:10;:25::i;:::-;10550:3;-1:-1:-1;;;;;10529:35:1;10546:1;-1:-1:-1;;;;;10529:35:1;;10555:8;10529:35;;;;;;;10770:214;10850:31;10864:6;10872:8;10850:13;:31::i;:::-;10891:33;10907:6;10915:8;10891:15;:33::i;:::-;10964:1;-1:-1:-1;;;;;10939:38:1;10948:6;-1:-1:-1;;;;;10939:38:1;;10968:8;10939:38;;;;;;;13124:375;13284:4;13371:13;13309:15;13320:3;13309:10;:15::i;:::-;13308:16;13304:58;;;13347:4;13340:11;;;;13304:58;13387:61;;;;;-1:-1:-1;;;;;13387:37:1;;;;;:61;;13425:5;;13432:8;;13442:5;;13387:61;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;13387:61:1;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;13387:61:1;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;13387:61:1;;;;;;;;;-1:-1:-1;;13466:25:1;;13476:15;13466:25;;-1:-1:-1;13371:77:1;-1:-1:-1;13124:375:1;;;;;;;;:::o;418:146:6:-;498:7;528:6;;;;521:14;;;;-1:-1:-1;552:5:6;;;418:146::o;570:166::-;650:7;682:5;;;704:6;;;;697:14;;;;728:1;570:166;-1:-1:-1;;;570:166:6:o;13505:634:1:-;13586:4;14037:17;;14124:8;;13505:634::o;5:118:-1:-;;72:46;110:6;97:20;72:46;;130:112;;194:43;229:6;216:20;194:43;;249:120;;326:38;356:6;350:13;326:38;;377:440;;471:4;459:17;;455:27;-1:-1;445:2;;496:1;493;486:12;445:2;533:6;520:20;555:64;570:48;611:6;570:48;;;555:64;;;546:73;;639:6;632:5;625:21;675:4;667:6;663:17;708:4;701:5;697:16;743:3;734:6;729:3;725:16;722:25;719:2;;;760:1;757;750:12;719:2;770:41;804:6;799:3;794;770:41;;;438:379;;;;;;;;825:118;;892:46;930:6;917:20;892:46;;950:241;;1054:2;1042:9;1033:7;1029:23;1025:32;1022:2;;;1070:1;1067;1060:12;1022:2;1105:1;1122:53;1167:7;1147:9;1122:53;;1198:366;;;1319:2;1307:9;1298:7;1294:23;1290:32;1287:2;;;1335:1;1332;1325:12;1287:2;1370:1;1387:53;1432:7;1412:9;1387:53;;;1377:63;;1349:97;1477:2;1495:53;1540:7;1531:6;1520:9;1516:22;1495:53;;;1485:63;;1456:98;1281:283;;;;;;1571:491;;;;1709:2;1697:9;1688:7;1684:23;1680:32;1677:2;;;1725:1;1722;1715:12;1677:2;1760:1;1777:53;1822:7;1802:9;1777:53;;;1767:63;;1739:97;1867:2;1885:53;1930:7;1921:6;1910:9;1906:22;1885:53;;;1875:63;;1846:98;1975:2;1993:53;2038:7;2029:6;2018:9;2014:22;1993:53;;;1983:63;;1954:98;1671:391;;;;;;2069:721;;;;;2233:3;2221:9;2212:7;2208:23;2204:33;2201:2;;;2250:1;2247;2240:12;2201:2;2285:1;2302:53;2347:7;2327:9;2302:53;;;2292:63;;2264:97;2392:2;2410:53;2455:7;2446:6;2435:9;2431:22;2410:53;;;2400:63;;2371:98;2500:2;2518:53;2563:7;2554:6;2543:9;2539:22;2518:53;;;2508:63;;2479:98;2636:2;2625:9;2621:18;2608:32;2660:18;2652:6;2649:30;2646:2;;;2692:1;2689;2682:12;2646:2;2712:62;2766:7;2757:6;2746:9;2742:22;2712:62;;;2702:72;;2587:193;2195:595;;;;;;;;2797:360;;;2915:2;2903:9;2894:7;2890:23;2886:32;2883:2;;;2931:1;2928;2921:12;2883:2;2966:1;2983:53;3028:7;3008:9;2983:53;;;2973:63;;2945:97;3073:2;3091:50;3133:7;3124:6;3113:9;3109:22;3091:50;;3164:366;;;3285:2;3273:9;3264:7;3260:23;3256:32;3253:2;;;3301:1;3298;3291:12;3253:2;3336:1;3353:53;3398:7;3378:9;3353:53;;;3343:63;;3315:97;3443:2;3461:53;3506:7;3497:6;3486:9;3482:22;3461:53;;3537:261;;3651:2;3639:9;3630:7;3626:23;3622:32;3619:2;;;3667:1;3664;3657:12;3619:2;3702:1;3719:63;3774:7;3754:9;3719:63;;3805:241;;3909:2;3897:9;3888:7;3884:23;3880:32;3877:2;;;3925:1;3922;3915:12;3877:2;3960:1;3977:53;4022:7;4002:9;3977:53;;4053:110;4126:31;4151:5;4126:31;;;4121:3;4114:44;4108:55;;;4170:101;4237:28;4259:5;4237:28;;4278:297;;4378:38;4410:5;4378:38;;;4433:6;4428:3;4421:19;4445:63;4501:6;4494:4;4489:3;4485:14;4478:4;4471:5;4467:16;4445:63;;;4540:29;4562:6;4540:29;;;4520:50;;;4533:4;4520:50;;4358:217;-1:-1;;;4358:217;4890:397;5045:2;5033:15;;5082:66;5077:2;5068:12;;5061:88;5183:66;5178:2;5169:12;;5162:88;5278:2;5269:12;;5026:261;5296:397;5451:2;5439:15;;5488:66;5483:2;5474:12;;5467:88;5589:66;5584:2;5575:12;;5568:88;5684:2;5675:12;;5432:261;5702:397;5857:2;5845:15;;5894:66;5889:2;5880:12;;5873:88;5995:66;5990:2;5981:12;;5974:88;6090:2;6081:12;;5838:261;6107:110;6180:31;6205:5;6180:31;;6224:193;6332:2;6317:18;;6346:61;6321:9;6380:6;6346:61;;6424:479;6606:2;6591:18;;6620:61;6595:9;6654:6;6620:61;;;6692:62;6750:2;6739:9;6735:18;6726:6;6692:62;;;6802:9;6796:4;6792:20;6787:2;6776:9;6772:18;6765:48;6827:66;6888:4;6879:6;6827:66;;;6819:74;6577:326;-1:-1;;;;;6577:326;6910:181;7012:2;6997:18;;7026:55;7001:9;7054:6;7026:55;;7098:281;7226:2;7240:47;;;7211:18;;7301:68;7211:18;7355:6;7301:68;;7386:387;7567:2;7581:47;;;7552:18;;7642:121;7552:18;7642:121;;7780:387;7961:2;7975:47;;;7946:18;;8036:121;7946:18;8036:121;;8174:387;8355:2;8369:47;;;8340:18;;8430:121;8340:18;8430:121;;8568:193;8676:2;8661:18;;8690:61;8665:9;8724:6;8690:61;;8768:256;8830:2;8824:9;8856:17;;;8931:18;8916:34;;8952:22;;;8913:62;8910:2;;;8988:1;8985;8978:12;8910:2;9004;8997:22;8808:216;;-1:-1;8808:216;9031:258;;9174:18;9166:6;9163:30;9160:2;;;9206:1;9203;9196:12;9160:2;-1:-1;9279:4;9250;9227:17;;;;-1:-1;;9223:33;9269:15;;9097:192;9296:91;9370:12;;9354:33;9493:128;-1:-1;;;;;9562:54;;9545:76;9628:92;9701:13;9694:21;;9677:43;9727:79;9796:5;9779:27;10047:151;-1:-1;;10115:78;;10098:100;10292:145;10373:6;10368:3;10363;10350:30;-1:-1;10429:1;10411:16;;10404:27;10343:94;10446:268;10511:1;10518:101;10532:6;10529:1;10526:13;10518:101;;;10599:11;;;10593:18;10580:11;;;10573:39;10554:2;10547:10;10518:101;;;10634:6;10631:1;10628:13;10625:2;;;-1:-1;;10699:1;10681:16;;10674:27;10495:219;10722:97;10810:2;10790:14;-1:-1;;10786:28;;10770:49" } } }, @@ -403,7 +421,7 @@ } }, "sourceCodes": { - "current/test/DummyERC721Token/DummyERC721Token.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 \"../../tokens/ERC721Token/ERC721Token.sol\";\nimport \"../../utils/Ownable/Ownable.sol\";\n\ncontract DummyERC721Token is\n Ownable,\n ERC721Token\n{\n\n /**\n * @dev Constructor passes its arguments to the base ERC721Token constructor\n * @param name of token\n * @param symbol of token\n */\n constructor (\n string name,\n string symbol\n )\n public\n ERC721Token(name, symbol)\n {}\n\n /**\n * @dev Function to mint a new token\n * @dev Reverts if the given token ID already exists\n * @param to address the beneficiary that will own the minted token\n * @param tokenId uint256 ID of the token to be minted by the msg.sender\n */\n function mint(address to, uint256 tokenId)\n public\n onlyOwner\n {\n require(\n !exists(tokenId),\n \"Token with tokenId already exists.\"\n );\n _mint(to, tokenId);\n }\n}\n", + "current/test/DummyERC721Token/DummyERC721Token.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 \"../../tokens/ERC721Token/ERC721Token.sol\";\nimport \"../../utils/Ownable/Ownable.sol\";\n\ncontract DummyERC721Token is\n Ownable,\n ERC721Token\n{\n\n /**\n * @dev Constructor passes its arguments to the base ERC721Token constructor\n * @param name of token\n * @param symbol of token\n */\n constructor (\n string name,\n string symbol\n )\n public\n ERC721Token(name, symbol)\n {}\n\n /**\n * @dev Function to mint a new token\n * @dev Reverts if the given token ID already exists\n * @param to address the beneficiary that will own the minted token\n * @param tokenId uint256 ID of the token to be minted by the msg.sender\n */\n function mint(address to, uint256 tokenId)\n public\n onlyOwner\n {\n require(\n !exists(tokenId),\n \"Token with tokenId already exists.\"\n );\n _mint(to, tokenId);\n }\n\n /**\n * @dev Function to burn a token\n * @dev Reverts if the given token ID doesn't exist\n * @param tokenId uint256 ID of the token to be minted by the msg.sender\n */\n function burn(address owner, uint256 tokenId)\n public\n onlyOwner\n {\n require(\n exists(tokenId),\n \"Token with tokenId does not exist.\"\n );\n _burn(owner, tokenId);\n }\n}\n", "current/tokens/ERC721Token/ERC721Token.sol": "/*\nThe MIT License (MIT)\n\nCopyright (c) 2016 Smart Contract Solutions, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n\npragma solidity ^0.4.24;\n\nimport \"./IERC721Token.sol\";\nimport \"./IERC721Receiver.sol\";\nimport \"../../utils/SafeMath/SafeMath.sol\";\n\n/**\n * @title ERC721 Non-Fungible Token Standard basic implementation\n * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md\n * Modified from https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC721/ERC721BasicToken.sol\n */\ncontract ERC721Token is\n IERC721Token,\n SafeMath\n{\n // Equals to `bytes4(keccak256(\"onERC721Received(address,uint256,bytes)\"))`\n // which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`\n bytes4 constant ERC721_RECEIVED = 0xf0b9e5ba;\n\n // Mapping from token ID to owner\n mapping (uint256 => address) internal tokenOwner;\n\n // Mapping from token ID to approved address\n mapping (uint256 => address) internal tokenApprovals;\n\n // Mapping from owner to number of owned token\n mapping (address => uint256) internal ownedTokensCount;\n\n // Mapping from owner to operator approvals\n mapping (address => mapping (address => bool)) internal operatorApprovals;\n\n /**\n * @dev Guarantees msg.sender is owner of the given token\n * @param _tokenId uint256 ID of the token to validate its ownership belongs to msg.sender\n */\n modifier onlyOwnerOf(uint256 _tokenId) {\n require(ownerOf(_tokenId) == msg.sender);\n _;\n }\n\n /**\n * @dev Checks msg.sender can transfer a token, by being owner, approved, or operator\n * @param _tokenId uint256 ID of the token to validate\n */\n modifier canTransfer(uint256 _tokenId) {\n require(isApprovedOrOwner(msg.sender, _tokenId));\n _;\n }\n\n function ERC721Token(\n string _name,\n string _symbol)\n public\n {\n name_ = _name;\n symbol_ = _symbol;\n }\n\n /**\n * @dev Gets the token name\n * @return string representing the token name\n */\n function name()\n public\n view\n returns (string)\n {\n return name_;\n }\n\n /**\n * @dev Gets the token symbol\n * @return string representing the token symbol\n */\n function symbol()\n public\n view\n returns (string)\n {\n return symbol_;\n }\n\n /**\n * @dev Gets the balance of the specified address\n * @param _owner address to query the balance of\n * @return uint256 representing the amount owned by the passed address\n */\n function balanceOf(address _owner)\n public\n view\n returns (uint256)\n {\n require(_owner != address(0));\n return ownedTokensCount[_owner];\n }\n\n /**\n * @dev Gets the owner of the specified token ID\n * @param _tokenId uint256 ID of the token to query the owner of\n * @return owner address currently marked as the owner of the given token ID\n */\n function ownerOf(uint256 _tokenId)\n public\n view\n returns (address)\n {\n address owner = tokenOwner[_tokenId];\n require(owner != address(0));\n return owner;\n }\n\n /**\n * @dev Returns whether the specified token exists\n * @param _tokenId uint256 ID of the token to query the existance of\n * @return whether the token exists\n */\n function exists(uint256 _tokenId)\n public\n view\n returns (bool)\n {\n address owner = tokenOwner[_tokenId];\n return owner != address(0);\n }\n\n /**\n * @dev Approves another address to transfer the given token ID\n * @dev The zero address indicates there is no approved address.\n * @dev There can only be one approved address per token at a given time.\n * @dev Can only be called by the token owner or an approved operator.\n * @param _to address to be approved for the given token ID\n * @param _tokenId uint256 ID of the token to be approved\n */\n function approve(address _to, uint256 _tokenId)\n public\n {\n address owner = ownerOf(_tokenId);\n require(_to != owner);\n require(msg.sender == owner || isApprovedForAll(owner, msg.sender));\n\n if (getApproved(_tokenId) != address(0) || _to != address(0)) {\n tokenApprovals[_tokenId] = _to;\n emit Approval(owner, _to, _tokenId);\n }\n }\n\n /**\n * @dev Gets the approved address for a token ID, or zero if no address set\n * @param _tokenId uint256 ID of the token to query the approval of\n * @return address currently approved for a the given token ID\n */\n function getApproved(uint256 _tokenId)\n public\n view\n returns (address)\n {\n return tokenApprovals[_tokenId];\n }\n\n /**\n * @dev Sets or unsets the approval of a given operator\n * @dev An operator is allowed to transfer all tokens of the sender on their behalf\n * @param _to operator address to set the approval\n * @param _approved representing the status of the approval to be set\n */\n function setApprovalForAll(address _to, bool _approved)\n public\n {\n require(_to != msg.sender);\n operatorApprovals[msg.sender][_to] = _approved;\n emit ApprovalForAll(msg.sender, _to, _approved);\n }\n\n /**\n * @dev Tells whether an operator is approved by a given owner\n * @param _owner owner address which you want to query the approval of\n * @param _operator operator address which you want to query the approval of\n * @return bool whether the given operator is approved by the given owner\n */\n function isApprovedForAll(address _owner, address _operator)\n public\n view\n returns (bool)\n {\n return operatorApprovals[_owner][_operator];\n }\n\n /**\n * @dev Transfers the ownership of a given token ID to another address\n * @dev Usage of this method is discouraged, use `safeTransferFrom` whenever possible\n * @dev Requires the msg sender to be the owner, approved, or operator\n * @param _from current owner of the token\n * @param _to address to receive the ownership of the given token ID\n * @param _tokenId uint256 ID of the token to be transferred\n */\n function transferFrom(address _from, address _to, uint256 _tokenId)\n public\n canTransfer(_tokenId)\n {\n require(_from != address(0));\n require(_to != address(0));\n\n clearApproval(_from, _tokenId);\n removeTokenFrom(_from, _tokenId);\n addTokenTo(_to, _tokenId);\n\n emit Transfer(_from, _to, _tokenId);\n }\n\n /**\n * @dev Safely transfers the ownership of a given token ID to another address\n * @dev If the target address is a contract, it must implement `onERC721Received`,\n * which is called upon a safe transfer, and return the magic value\n * `bytes4(keccak256(\"onERC721Received(address,uint256,bytes)\"))`; otherwise,\n * the transfer is reverted.\n * @dev Requires the msg sender to be the owner, approved, or operator\n * @param _from current owner of the token\n * @param _to address to receive the ownership of the given token ID\n * @param _tokenId uint256 ID of the token to be transferred\n */\n function safeTransferFrom(\n address _from,\n address _to,\n uint256 _tokenId)\n public\n canTransfer(_tokenId)\n {\n // solium-disable-next-line arg-overflow\n safeTransferFrom(_from, _to, _tokenId, \"\");\n }\n\n /**\n * @dev Safely transfers the ownership of a given token ID to another address\n * @dev If the target address is a contract, it must implement `onERC721Received`,\n * which is called upon a safe transfer, and return the magic value\n * `bytes4(keccak256(\"onERC721Received(address,uint256,bytes)\"))`; otherwise,\n * the transfer is reverted.\n * @dev Requires the msg sender to be the owner, approved, or operator\n * @param _from current owner of the token\n * @param _to address to receive the ownership of the given token ID\n * @param _tokenId uint256 ID of the token to be transferred\n * @param _data bytes data to send along with a safe transfer check\n */\n function safeTransferFrom(\n address _from,\n address _to,\n uint256 _tokenId,\n bytes _data)\n public\n canTransfer(_tokenId)\n {\n transferFrom(_from, _to, _tokenId);\n // solium-disable-next-line arg-overflow\n require(checkAndCallSafeTransfer(_from, _to, _tokenId, _data));\n }\n\n /**\n * @dev Returns whether the given spender can transfer a given token ID\n * @param _spender address of the spender to query\n * @param _tokenId uint256 ID of the token to be transferred\n * @return bool whether the msg.sender is approved for the given token ID,\n * is an operator of the owner, or is the owner of the token\n */\n function isApprovedOrOwner(address _spender, uint256 _tokenId)\n internal\n view\n returns (bool)\n {\n address owner = ownerOf(_tokenId);\n return _spender == owner || getApproved(_tokenId) == _spender || isApprovedForAll(owner, _spender);\n }\n\n /**\n * @dev Internal function to mint a new token\n * @dev Reverts if the given token ID already exists\n * @param _to The address that will own the minted token\n * @param _tokenId uint256 ID of the token to be minted by the msg.sender\n */\n function _mint(address _to, uint256 _tokenId)\n internal\n {\n require(_to != address(0));\n addTokenTo(_to, _tokenId);\n emit Transfer(address(0), _to, _tokenId);\n }\n\n /**\n * @dev Internal function to burn a specific token\n * @dev Reverts if the token does not exist\n * @param _tokenId uint256 ID of the token being burned by the msg.sender\n */\n function _burn(address _owner, uint256 _tokenId)\n internal\n {\n clearApproval(_owner, _tokenId);\n removeTokenFrom(_owner, _tokenId);\n emit Transfer(_owner, address(0), _tokenId);\n }\n\n /**\n * @dev Internal function to clear current approval of a given token ID\n * @dev Reverts if the given address is not indeed the owner of the token\n * @param _owner owner of the token\n * @param _tokenId uint256 ID of the token to be transferred\n */\n function clearApproval(address _owner, uint256 _tokenId)\n internal\n {\n require(ownerOf(_tokenId) == _owner);\n if (tokenApprovals[_tokenId] != address(0)) {\n tokenApprovals[_tokenId] = address(0);\n emit Approval(_owner, address(0), _tokenId);\n }\n }\n\n /**\n * @dev Internal function to add a token ID to the list of a given address\n * @param _to address representing the new owner of the given token ID\n * @param _tokenId uint256 ID of the token to be added to the tokens list of the given address\n */\n function addTokenTo(address _to, uint256 _tokenId)\n internal\n {\n require(tokenOwner[_tokenId] == address(0));\n tokenOwner[_tokenId] = _to;\n ownedTokensCount[_to] = safeAdd(ownedTokensCount[_to], 1);\n }\n\n /**\n * @dev Internal function to remove a token ID from the list of a given address\n * @param _from address representing the previous owner of the given token ID\n * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address\n */\n function removeTokenFrom(address _from, uint256 _tokenId)\n internal\n {\n require(ownerOf(_tokenId) == _from);\n ownedTokensCount[_from] = safeSub(ownedTokensCount[_from], 1);\n tokenOwner[_tokenId] = address(0);\n }\n\n /**\n * @dev Internal function to invoke `onERC721Received` on a target address\n * @dev The call is not executed if the target address is not a contract\n * @param _from address representing the previous owner of the given token ID\n * @param _to target address that will receive the tokens\n * @param _tokenId uint256 ID of the token to be transferred\n * @param _data bytes optional data to send along with the call\n * @return whether the call correctly returned the expected magic value\n */\n function checkAndCallSafeTransfer(\n address _from,\n address _to,\n uint256 _tokenId,\n bytes _data)\n internal\n returns (bool)\n {\n if (!isContract(_to)) {\n return true;\n }\n bytes4 retval = IERC721Receiver(_to).onERC721Received(_from, _tokenId, _data);\n return (retval == ERC721_RECEIVED);\n }\n\n function isContract(address addr)\n internal\n view\n returns (bool)\n {\n uint256 size;\n // XXX Currently there is no better way to check if there is a contract in an address\n // than to check the size of the code at that address.\n // See https://ethereum.stackexchange.com/a/14016/36603\n // for more details about how this works.\n // TODO Check this again before the Serenity release, because all addresses will be\n // contracts then.\n assembly { size := extcodesize(addr) } // solium-disable-line security/no-inline-assembly\n return size > 0;\n }\n}\n", "current/tokens/ERC721Token/IERC721Receiver.sol": "/*\nThe MIT License (MIT)\n\nCopyright (c) 2016 Smart Contract Solutions, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n\npragma solidity ^0.4.24;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * rom ERC721 asset contracts.\n * Modified from https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC721/ERC721Receiver.sol\n */\ncontract IERC721Receiver {\n /**\n * @dev Magic value to be returned upon successful reception of an NFT\n * Equals to `bytes4(keccak256(\"onERC721Received(address,uint256,bytes)\"))`,\n * which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`\n */\n bytes4 constant ERC721_RECEIVED = 0xf0b9e5ba;\n\n /**\n * @notice Handle the receipt of an NFT\n * @dev The ERC721 smart contract calls this function on the recipient\n * after a `safetransfer`. This function MAY throw to revert and reject the\n * transfer. This function MUST use 50,000 gas or less. Return of other\n * than the magic value MUST result in the transaction being reverted.\n * Note: the contract address is always the message sender.\n * @param _from The sending address\n * @param _tokenId The NFT identifier which is being transfered\n * @param _data Additional data with no specified format\n * @return `bytes4(keccak256(\"onERC721Received(address,uint256,bytes)\"))`\n */\n function onERC721Received(\n address _from,\n uint256 _tokenId,\n bytes _data)\n public\n returns (bytes4);\n}\n", "current/tokens/ERC721Token/IERC721Token.sol": "/*\nThe MIT License (MIT)\n\nCopyright (c) 2016 Smart Contract Solutions, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n\npragma solidity ^0.4.24;\n\n/**\n * @title ERC721 Non-Fungible Token Standard basic interface\n * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md\n * Modified from https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC721/ERC721Basic.sol\n */\ncontract IERC721Token {\n string internal name_;\n string internal symbol_;\n\n event Transfer(\n address indexed _from,\n address indexed _to,\n uint256 _tokenId\n );\n event Approval(\n address indexed _owner,\n address indexed _approved,\n uint256 _tokenId\n );\n event ApprovalForAll(\n address indexed _owner,\n address indexed _operator,\n bool _approved\n );\n\n function name()\n public\n view\n returns (string);\n function symbol()\n public\n view\n returns (string);\n\n function balanceOf(address _owner)\n public\n view\n returns (uint256 _balance);\n function ownerOf(uint256 _tokenId)\n public\n view\n returns (address _owner);\n function exists(uint256 _tokenId)\n public\n view\n returns (bool _exists);\n\n function approve(address _to, uint256 _tokenId)\n public;\n function getApproved(uint256 _tokenId)\n public\n view\n returns (address _operator);\n\n function setApprovalForAll(address _operator, bool _approved)\n public;\n function isApprovedForAll(address _owner, address _operator)\n public\n view\n returns (bool);\n\n function transferFrom(\n address _from,\n address _to,\n uint256 _tokenId)\n public;\n function safeTransferFrom(\n address _from,\n address _to,\n uint256 _tokenId)\n public;\n function safeTransferFrom(\n address _from,\n address _to,\n uint256 _tokenId,\n bytes _data)\n public;\n}\n", @@ -411,7 +429,7 @@ "current/utils/Ownable/Ownable.sol": "pragma solidity ^0.4.24;\npragma experimental ABIEncoderV2;\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\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 is allowed to call this method.\"\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", "current/utils/SafeMath/SafeMath.sol": "pragma solidity ^0.4.24;\npragma experimental ABIEncoderV2;\n\ncontract SafeMath {\n function safeMul(uint a, uint b)\n internal\n pure\n returns (uint256)\n {\n uint c = a * b;\n assert(a == 0 || c / a == b);\n return c;\n }\n\n function safeDiv(uint a, uint b)\n internal\n pure\n returns (uint256)\n {\n uint c = a / b;\n return c;\n }\n\n function safeSub(uint a, uint b)\n internal\n pure\n returns (uint256)\n {\n assert(b <= a);\n return a - b;\n }\n\n function safeAdd(uint a, uint b)\n internal\n pure\n returns (uint256)\n {\n uint c = a + b;\n assert(c >= a);\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": "0xd868b50be7aa97eabea9bd6a18b16340a24e2b30ea7ab0ab76f34342dadccbf1", + "sourceTreeHashHex": "0xd6bb686ff65d23302296d9d4f80d38d0fb8cb970b8d10ffdd36d8befbcd1803d", "compiler": { "name": "solc", "version": "soljson-v0.4.24+commit.e67f0147.js", diff --git a/packages/order-utils/src/exchange_transfer_simulator.ts b/packages/order-utils/src/exchange_transfer_simulator.ts index cac4af243..a70259fcd 100644 --- a/packages/order-utils/src/exchange_transfer_simulator.ts +++ b/packages/order-utils/src/exchange_transfer_simulator.ts @@ -91,12 +91,11 @@ export class ExchangeTransferSimulator { amountInBaseUnits: BigNumber, ): Promise { const assetProxyId = assetProxyUtils.decodeAssetDataId(assetData); - const isERC721Asset = assetProxyId === AssetProxyId.ERC721; const proxyAllowance = await this._store.getProxyAllowanceAsync(assetData, userAddress); // HACK: This code assumes that all tokens with an UNLIMITED_ALLOWANCE_IN_BASE_UNITS set, // are UnlimitedAllowanceTokens. This is however not true, it just so happens that all // DummyERC20Tokens we use in tests are. - if (!proxyAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS) && !isERC721Asset) { + if (!proxyAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) { this._store.setProxyAllowance(assetData, userAddress, proxyAllowance.minus(amountInBaseUnits)); } } -- cgit From fb55def54f44413a8b0a7f001fb18d6eac89e422 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 15 Jun 2018 00:03:00 +0200 Subject: Add ability to tweak the relevant balances/allowances for the maker and taker for a fillScenario. Convert more of the core tests to the declarative form. --- .../src/utils/core_combinatorial_utils.ts | 343 ++++++++++++++++++--- packages/contracts/src/utils/types.ts | 15 + packages/contracts/test/exchange/core.ts | 43 --- packages/contracts/test/exchange/fill_order.ts | 57 ++++ 4 files changed, 380 insertions(+), 78 deletions(-) diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index 776d7b2fe..1735b3191 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -38,13 +38,13 @@ import { OrderScenario, TakerAssetFillAmountScenario, TakerScenario, + TokenAmountScenario, + TraderStateScenario, } from '../utils/types'; chaiSetup.configure(); const expect = chai.expect; -const ERC721_PROXY_ID = 2; - /** * Instantiates a new instance of CoreCombinatorialUtils. Since this method has some * required async setup, a factory method is required. @@ -192,6 +192,18 @@ export class CoreCombinatorialUtils { takerAssetDataScenario: fillScenarioArray[8] as AssetDataScenario, }, takerAssetFillAmountScenario: fillScenarioArray[9] as TakerAssetFillAmountScenario, + makerStateScenario: { + traderAssetBalance: TokenAmountScenario.Higher, + traderAssetAllowance: TokenAmountScenario.Higher, + zrxFeeBalance: TokenAmountScenario.Higher, + zrxFeeAllowance: TokenAmountScenario.Higher, + }, + takerStateScenario: { + traderAssetBalance: TokenAmountScenario.Higher, + traderAssetAllowance: TokenAmountScenario.Higher, + zrxFeeBalance: TokenAmountScenario.Higher, + zrxFeeAllowance: TokenAmountScenario.Higher, + }, }; return fillScenario; }); @@ -248,24 +260,305 @@ export class CoreCombinatorialUtils { signature: `0x${signature.toString('hex')}`, }; - // 3. Permutate the maker and taker balance/allowance scenarios - // TODO(fabio) - - // 4. Figure out fill amount const balanceAndProxyAllowanceFetcher = new SimpleAssetBalanceAndProxyAllowanceFetcher(this.assetWrapper); const orderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher( this.exchangeWrapper, this.zrxAssetData, ); - const orderStateUtils = new OrderStateUtils(balanceAndProxyAllowanceFetcher, orderFilledCancelledFetcher); + // 3. Figure out fill amount + const takerAssetFillAmount = await this._getTakerAssetFillAmountAsync( + signedOrder, + fillScenario.takerAssetFillAmountScenario, + balanceAndProxyAllowanceFetcher, + orderFilledCancelledFetcher, + ); + + // 4. Permutate the maker and taker balance/allowance scenarios + await this._modifyTraderStateAsync( + fillScenario.makerStateScenario, + fillScenario.takerStateScenario, + signedOrder, + takerAssetFillAmount, + balanceAndProxyAllowanceFetcher, + ); + + // 5. If I fill it by X, what are the resulting balances/allowances/filled amounts exp? + const orderValidationUtils = new OrderValidationUtils(orderFilledCancelledFetcher); + const lazyStore = new BalanceAndProxyAllowanceLazyStore(balanceAndProxyAllowanceFetcher); + const exchangeTransferSimulator = new ExchangeTransferSimulator(lazyStore); + + let isFillFailureExpected = false; + try { + await orderValidationUtils.validateFillOrderThrowIfInvalidAsync( + exchangeTransferSimulator, + provider, + signedOrder, + takerAssetFillAmount, + this.takerAddress, + this.zrxAssetData, + ); + } catch (err) { + isFillFailureExpected = true; + } + + await this._fillOrderAndAssertOutcomeAsync( + signedOrder, + takerAssetFillAmount, + lazyStore, + isFillFailureExpected, + provider, + ); + } + private async _modifyTraderStateAsync( + makerStateScenario: TraderStateScenario, + takerStateScenario: TraderStateScenario, + signedOrder: SignedOrder, + takerAssetFillAmount: BigNumber, + balanceAndProxyAllowanceFetcher: SimpleAssetBalanceAndProxyAllowanceFetcher, + ): Promise { + const makerAssetFillAmount = orderUtils.getPartialAmount( + takerAssetFillAmount, + signedOrder.takerAssetAmount, + signedOrder.makerAssetAmount, + ); + switch (makerStateScenario.traderAssetBalance) { + case TokenAmountScenario.Higher: + break; // Noop since this is already the default + + case TokenAmountScenario.TooLow: + if (makerAssetFillAmount.eq(0)) { + throw new Error(`Cannot set makerAssetBalanceOfMaker TooLow if makerAssetFillAmount is 0`); + } + const tooLowBalance = makerAssetFillAmount.minus(1); + await this.assetWrapper.setBalanceAsync( + signedOrder.makerAddress, + signedOrder.makerAssetData, + tooLowBalance, + ); + break; + + case TokenAmountScenario.Exact: + const exactBalance = makerAssetFillAmount; + await this.assetWrapper.setBalanceAsync( + signedOrder.makerAddress, + signedOrder.makerAssetData, + exactBalance, + ); + break; + + default: + throw errorUtils.spawnSwitchErr( + 'makerStateScenario.traderAssetBalance', + makerStateScenario.traderAssetBalance, + ); + } + + const makerFee = orderUtils.getPartialAmount( + takerAssetFillAmount, + signedOrder.takerAssetAmount, + signedOrder.makerFee, + ); + switch (makerStateScenario.zrxFeeBalance) { + case TokenAmountScenario.Higher: + break; // Noop since this is already the default + + case TokenAmountScenario.TooLow: + if (makerFee.eq(0)) { + throw new Error(`Cannot set zrxAsserBalanceOfMaker TooLow if makerFee is 0`); + } + const tooLowBalance = makerFee.minus(1); + await this.assetWrapper.setBalanceAsync(signedOrder.makerAddress, this.zrxAssetData, tooLowBalance); + break; + + case TokenAmountScenario.Exact: + const exactBalance = makerFee; + await this.assetWrapper.setBalanceAsync(signedOrder.makerAddress, this.zrxAssetData, exactBalance); + break; + + default: + throw errorUtils.spawnSwitchErr('makerStateScenario.zrxFeeBalance', makerStateScenario.zrxFeeBalance); + } + + switch (makerStateScenario.traderAssetAllowance) { + case TokenAmountScenario.Higher: + break; // Noop since this is already the default + + case TokenAmountScenario.TooLow: + const tooLowAllowance = makerAssetFillAmount.minus(1); + await this.assetWrapper.setProxyAllowanceAsync( + signedOrder.makerAddress, + signedOrder.makerAssetData, + tooLowAllowance, + ); + break; + + case TokenAmountScenario.Exact: + const exactAllowance = makerAssetFillAmount; + await this.assetWrapper.setProxyAllowanceAsync( + signedOrder.makerAddress, + signedOrder.makerAssetData, + exactAllowance, + ); + break; + + default: + throw errorUtils.spawnSwitchErr( + 'makerStateScenario.traderAssetAllowance', + makerStateScenario.traderAssetAllowance, + ); + } + + switch (makerStateScenario.zrxFeeAllowance) { + case TokenAmountScenario.Higher: + break; // Noop since this is already the default + + case TokenAmountScenario.TooLow: + const tooLowAllowance = makerFee.minus(1); + await this.assetWrapper.setProxyAllowanceAsync( + signedOrder.makerAddress, + this.zrxAssetData, + tooLowAllowance, + ); + break; + + case TokenAmountScenario.Exact: + const exactAllowance = makerFee; + await this.assetWrapper.setProxyAllowanceAsync( + signedOrder.makerAddress, + this.zrxAssetData, + exactAllowance, + ); + break; + + default: + throw errorUtils.spawnSwitchErr( + 'makerStateScenario.zrxFeeAllowance', + makerStateScenario.zrxFeeAllowance, + ); + } + + switch (takerStateScenario.traderAssetBalance) { + case TokenAmountScenario.Higher: + break; // Noop since this is already the default + + case TokenAmountScenario.TooLow: + if (takerAssetFillAmount.eq(0)) { + throw new Error(`Cannot set takerAssetBalanceOfTaker TooLow if takerAssetFillAmount is 0`); + } + const tooLowBalance = takerAssetFillAmount.minus(1); + await this.assetWrapper.setBalanceAsync(this.takerAddress, signedOrder.takerAssetData, tooLowBalance); + break; + + case TokenAmountScenario.Exact: + const exactBalance = takerAssetFillAmount; + await this.assetWrapper.setBalanceAsync(this.takerAddress, signedOrder.takerAssetData, exactBalance); + break; + + default: + throw errorUtils.spawnSwitchErr( + 'takerStateScenario.traderAssetBalance', + takerStateScenario.traderAssetBalance, + ); + } + + const takerFee = orderUtils.getPartialAmount( + takerAssetFillAmount, + signedOrder.takerAssetAmount, + signedOrder.takerFee, + ); + switch (takerStateScenario.zrxFeeBalance) { + case TokenAmountScenario.Higher: + break; // Noop since this is already the default + + case TokenAmountScenario.TooLow: + if (takerFee.eq(0)) { + throw new Error(`Cannot set zrxAssetBalanceOfTaker TooLow if takerFee is 0`); + } + const tooLowBalance = takerFee.minus(1); + await this.assetWrapper.setBalanceAsync(this.takerAddress, this.zrxAssetData, tooLowBalance); + break; + + case TokenAmountScenario.Exact: + const exactBalance = takerFee; + await this.assetWrapper.setBalanceAsync(this.takerAddress, this.zrxAssetData, exactBalance); + break; + + default: + throw errorUtils.spawnSwitchErr('takerStateScenario.zrxFeeBalance', takerStateScenario.zrxFeeBalance); + } + + switch (takerStateScenario.traderAssetAllowance) { + case TokenAmountScenario.Higher: + break; // Noop since this is already the default + + case TokenAmountScenario.TooLow: + const tooLowAllowance = takerAssetFillAmount.minus(1); + await this.assetWrapper.setProxyAllowanceAsync( + this.takerAddress, + signedOrder.takerAssetData, + tooLowAllowance, + ); + break; + + case TokenAmountScenario.Exact: + const exactAllowance = takerAssetFillAmount; + await this.assetWrapper.setProxyAllowanceAsync( + this.takerAddress, + signedOrder.takerAssetData, + exactAllowance, + ); + break; + + default: + throw errorUtils.spawnSwitchErr( + 'takerStateScenario.traderAssetAllowance', + takerStateScenario.traderAssetAllowance, + ); + } + + switch (takerStateScenario.zrxFeeAllowance) { + case TokenAmountScenario.Higher: + break; // Noop since this is already the default + + case TokenAmountScenario.TooLow: + const tooLowAllowance = takerFee.minus(1); + await this.assetWrapper.setProxyAllowanceAsync( + signedOrder.takerAddress, + this.zrxAssetData, + tooLowAllowance, + ); + break; + + case TokenAmountScenario.Exact: + const exactAllowance = takerFee; + await this.assetWrapper.setProxyAllowanceAsync( + signedOrder.takerAddress, + this.zrxAssetData, + exactAllowance, + ); + break; + + default: + throw errorUtils.spawnSwitchErr( + 'takerStateScenario.zrxFeeAllowance', + takerStateScenario.zrxFeeAllowance, + ); + } + } + private async _getTakerAssetFillAmountAsync( + signedOrder: SignedOrder, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario, + balanceAndProxyAllowanceFetcher: SimpleAssetBalanceAndProxyAllowanceFetcher, + orderFilledCancelledFetcher: SimpleOrderFilledCancelledFetcher, + ): Promise { + const orderStateUtils = new OrderStateUtils(balanceAndProxyAllowanceFetcher, orderFilledCancelledFetcher); const fillableTakerAssetAmount = await orderStateUtils.getMaxFillableTakerAssetAmountAsync( signedOrder, this.takerAddress, ); let takerAssetFillAmount; - const takerAssetFillAmountScenario = fillScenario.takerAssetFillAmountScenario; switch (takerAssetFillAmountScenario) { case TakerAssetFillAmountScenario.Zero: takerAssetFillAmount = new BigNumber(0); @@ -283,7 +576,7 @@ export class CoreCombinatorialUtils { const takerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.takerAssetData); const makerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.makerAssetData); const isEitherAssetERC721 = - takerAssetProxyId === ERC721_PROXY_ID || makerAssetProxyId === ERC721_PROXY_ID; + takerAssetProxyId === constants.ERC721_PROXY_ID || makerAssetProxyId === constants.ERC721_PROXY_ID; if (isEitherAssetERC721) { throw new Error( 'Cannot test `TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount` together with ERC721 assets since orders involving ERC721 must always be filled exactly.', @@ -296,32 +589,7 @@ export class CoreCombinatorialUtils { throw errorUtils.spawnSwitchErr('TakerAssetFillAmountScenario', takerAssetFillAmountScenario); } - // 5. If I fill it by X, what are the resulting balances/allowances/filled amounts exp? - const orderValidationUtils = new OrderValidationUtils(orderFilledCancelledFetcher); - const lazyStore = new BalanceAndProxyAllowanceLazyStore(balanceAndProxyAllowanceFetcher); - const exchangeTransferSimulator = new ExchangeTransferSimulator(lazyStore); - - let isFillFailureExpected = false; - try { - await orderValidationUtils.validateFillOrderThrowIfInvalidAsync( - exchangeTransferSimulator, - provider, - signedOrder, - takerAssetFillAmount, - this.takerAddress, - this.zrxAssetData, - ); - } catch (err) { - isFillFailureExpected = true; - } - - await this._fillOrderAndAssertOutcomeAsync( - signedOrder, - takerAssetFillAmount, - lazyStore, - isFillFailureExpected, - provider, - ); + return takerAssetFillAmount; } private async _fillOrderAndAssertOutcomeAsync( signedOrder: SignedOrder, @@ -369,6 +637,11 @@ export class CoreCombinatorialUtils { signedOrder.makerAssetAmount, ); + const beforeMakerAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync( + makerAddress, + makerAssetData, + ); + // - Let's fill the order! const txReceipt = await this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { takerAssetFillAmount, diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index f0e2dde02..d71c14495 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -201,7 +201,22 @@ export interface OrderScenario { takerAssetDataScenario: AssetDataScenario; } +export enum TokenAmountScenario { + Exact = 'EXACT', + TooLow = 'TOO_LOW', + Higher = 'HIGHER', +} + +export interface TraderStateScenario { + traderAssetBalance: TokenAmountScenario; + traderAssetAllowance: TokenAmountScenario; + zrxFeeBalance: TokenAmountScenario; + zrxFeeAllowance: TokenAmountScenario; +} + export interface FillScenario { orderScenario: OrderScenario; takerAssetFillAmountScenario: TakerAssetFillAmountScenario; + makerStateScenario: TraderStateScenario; + takerStateScenario: TraderStateScenario; } diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index 193754e70..453f05df2 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -148,49 +148,6 @@ describe('Exchange core', () => { ); }); - it('should throw if maker erc20Balances are too low to fill order', async () => { - signedOrder = orderFactory.newSignedOrder({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18), - }); - - return expectRevertOrAlwaysFailingTransactionAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - ); - }); - - it('should throw if taker erc20Balances are too low to fill order', async () => { - signedOrder = orderFactory.newSignedOrder({ - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18), - }); - return expectRevertOrAlwaysFailingTransactionAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - ); - }); - - it('should throw if maker allowances are too low to fill order', async () => { - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, new BigNumber(0), { - from: makerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - return expectRevertOrAlwaysFailingTransactionAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - ); - }); - - it('should throw if taker allowances are too low to fill order', async () => { - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20TokenB.approve.sendTransactionAsync(erc20Proxy.address, new BigNumber(0), { - from: takerAddress, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - return expectRevertOrAlwaysFailingTransactionAsync( - exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), - ); - }); - it('should throw if no value is filled', async () => { signedOrder = orderFactory.newSignedOrder(); await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); diff --git a/packages/contracts/test/exchange/fill_order.ts b/packages/contracts/test/exchange/fill_order.ts index 2498ee27e..5e8dd6ffc 100644 --- a/packages/contracts/test/exchange/fill_order.ts +++ b/packages/contracts/test/exchange/fill_order.ts @@ -13,6 +13,7 @@ import { OrderScenario, TakerAssetFillAmountScenario, TakerScenario, + TokenAmountScenario, } from '../../src/utils/types'; chaiSetup.configure(); @@ -31,6 +32,18 @@ const defaultFillScenario = { takerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, }, takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, + makerStateScenario: { + traderAssetBalance: TokenAmountScenario.Higher, + traderAssetAllowance: TokenAmountScenario.Higher, + zrxFeeBalance: TokenAmountScenario.Higher, + zrxFeeAllowance: TokenAmountScenario.Higher, + }, + takerStateScenario: { + traderAssetBalance: TokenAmountScenario.Higher, + traderAssetAllowance: TokenAmountScenario.Higher, + zrxFeeBalance: TokenAmountScenario.Higher, + zrxFeeAllowance: TokenAmountScenario.Higher, + }, }; describe('FillOrder Tests', () => { @@ -144,6 +157,50 @@ describe('FillOrder Tests', () => { }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); + + it('should throw if maker erc20Balances are too low to fill order', async () => { + const fillScenario = { + ...defaultFillScenario, + makerStateScenario: { + ...defaultFillScenario.makerStateScenario, + traderAssetBalance: TokenAmountScenario.TooLow, + }, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + + it('should throw if taker erc20Balances are too low to fill order', async () => { + const fillScenario = { + ...defaultFillScenario, + takerStateScenario: { + ...defaultFillScenario.makerStateScenario, + traderAssetBalance: TokenAmountScenario.TooLow, + }, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + + it('should throw if maker allowances are too low to fill order', async () => { + const fillScenario = { + ...defaultFillScenario, + makerStateScenario: { + ...defaultFillScenario.makerStateScenario, + traderAssetAllowance: TokenAmountScenario.TooLow, + }, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + + it('should throw if taker allowances are too low to fill order', async () => { + const fillScenario = { + ...defaultFillScenario, + takerStateScenario: { + ...defaultFillScenario.makerStateScenario, + traderAssetAllowance: TokenAmountScenario.TooLow, + }, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); }); describe('Testing Exchange of ERC721 Tokens', () => { -- cgit From 1a0b9e46120dd32b3df3bca5076b26ed8a9c932b Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 19 Jun 2018 17:25:08 +0200 Subject: Comments and cleanup --- packages/contracts/src/utils/asset_wrapper.ts | 28 ++++++++------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/packages/contracts/src/utils/asset_wrapper.ts b/packages/contracts/src/utils/asset_wrapper.ts index 462a5086a..2eb54f290 100644 --- a/packages/contracts/src/utils/asset_wrapper.ts +++ b/packages/contracts/src/utils/asset_wrapper.ts @@ -12,6 +12,10 @@ interface ProxyIdToAssetWrappers { [proxyId: number]: AbstractAssetWrapper; } +/** + * This class abstracts away the differences between ERC20 and ERC721 tokens so that + * the logic that uses it does not need to care what standard a token belongs to. + */ export class AssetWrapper { private _proxyIdToAssetWrappers: ProxyIdToAssetWrappers; constructor(assetWrappers: AbstractAssetWrapper[]) { @@ -29,7 +33,6 @@ export class AssetWrapper { const balance = await assetWrapper.getBalanceAsync(userAddress, assetData); return balance; } - case constants.ERC721_PROXY_ID: { const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper; const assetProxyData = assetProxyUtils.decodeERC721AssetData(assetData); @@ -41,7 +44,6 @@ export class AssetWrapper { const balance = isOwner ? new BigNumber(1) : new BigNumber(0); return balance; } - default: throw errorUtils.spawnSwitchErr('proxyId', proxyId); } @@ -54,7 +56,6 @@ export class AssetWrapper { await assetWrapper.setBalanceAsync(userAddress, assetData, desiredBalance); return; } - case constants.ERC721_PROXY_ID: { if (!desiredBalance.eq(0) && !desiredBalance.eq(1)) { throw new Error(`Balance for ERC721 token can only be set to 0 or 1. Got: ${desiredBalance}`); @@ -82,11 +83,6 @@ export class AssetWrapper { tokenOwner, userAddress, ); - } else if ( - (userAddress !== tokenOwner && desiredBalance.eq(0)) || - (tokenOwner === userAddress && desiredBalance.eq(1)) - ) { - return; // noop } else if (tokenOwner === userAddress && desiredBalance.eq(0)) { // Burn token await erc721Wrapper.burnAsync(assetProxyData.tokenAddress, assetProxyData.tokenId, userAddress); @@ -94,7 +90,6 @@ export class AssetWrapper { } break; } - default: throw errorUtils.spawnSwitchErr('proxyId', proxyId); } @@ -107,7 +102,6 @@ export class AssetWrapper { const allowance = await assetWrapper.getProxyAllowanceAsync(userAddress, assetData); return allowance; } - case constants.ERC721_PROXY_ID: { const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper; const erc721ProxyData = assetProxyUtils.decodeERC721AssetData(assetData); @@ -123,7 +117,6 @@ export class AssetWrapper { const allowance = isProxyApproved || isProxyApprovedForAllAsync ? new BigNumber(1) : new BigNumber(0); return allowance; } - default: throw errorUtils.spawnSwitchErr('proxyId', proxyId); } @@ -140,7 +133,6 @@ export class AssetWrapper { await assetWrapper.setAllowanceAsync(userAddress, assetData, desiredAllowance); return; } - case constants.ERC721_PROXY_ID: { if (!desiredAllowance.eq(0) && !desiredAllowance.eq(1)) { throw new Error(`Allowance for ERC721 token can only be set to 0 or 1. Got: ${desiredAllowance}`); @@ -164,8 +156,9 @@ export class AssetWrapper { assetProxyData.tokenAddress, assetProxyData.tokenId, ); - // TODO: We should have a way to deal with this. Things get hairier once we are testing - // batch fills + // HACK: We do not currently support ApprovedForAll when setting proxy allowance + // This was intentional since unsetting ApprovedForAll, will unset approval for unrelated + // tokens other then the one specified in the call to this method. if (isProxyApprovedForAll) { throw new Error(`We don't currently support the use of "approveAll" functionality for ERC721.`); } @@ -177,21 +170,16 @@ export class AssetWrapper { if (!isProxyApproved && desiredAllowance.eq(1)) { await erc721Wrapper.approveProxyAsync(assetProxyData.tokenAddress, assetProxyData.tokenId); } else if (isProxyApproved && desiredAllowance.eq(0)) { + // Remove approval await erc721Wrapper.approveAsync( constants.NULL_ADDRESS, assetProxyData.tokenAddress, assetProxyData.tokenId, ); - } else if ( - (!isProxyApproved && desiredAllowance.eq(0)) || - (isProxyApproved && desiredAllowance.eq(1)) - ) { - return; // noop } break; } - default: throw errorUtils.spawnSwitchErr('proxyId', proxyId); } -- cgit From 70de264d4dd96efce2719c209c4b866c93668877 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 19 Jun 2018 17:31:26 +0200 Subject: Cleanup coreCombinatorialUtils --- .../src/utils/core_combinatorial_utils.ts | 446 +++++++++++---------- 1 file changed, 226 insertions(+), 220 deletions(-) diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index 1735b3191..d6142c5fb 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -11,7 +11,6 @@ import { BigNumber, errorUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; import { BlockParamLiteral, LogWithDecodedArgs, Provider, TxData } from 'ethereum-types'; -// import ethUtil = require('ethereumjs-util'); import * as _ from 'lodash'; import 'make-promises-safe'; @@ -165,7 +164,7 @@ export class CoreCombinatorialUtils { AssetDataScenario.ZRXFeeToken, ]; const takerAssetFillAmountScenario = [TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount]; - const fillScenarioArrays = CoreCombinatorialUtils._allPossibleCases([ + const fillScenarioArrays = CoreCombinatorialUtils._getAllCombinations([ takerScenarios, feeRecipientScenarios, makerAssetAmountScenario, @@ -210,19 +209,25 @@ export class CoreCombinatorialUtils { return fillScenarios; } - private static _allPossibleCases(arrays: string[][]): string[][] { + /** + * Recursive implementation of generating all combinations of the supplied + * string-containing arrays. + */ + private static _getAllCombinations(arrays: string[][]): string[][] { + // Base case if (arrays.length === 1) { - const remainingVals = _.map(arrays[0], val => { + const remainingValues = _.map(arrays[0], val => { return [val]; }); - return remainingVals; + return remainingValues; } else { const result = []; - const allCasesOfRest = CoreCombinatorialUtils._allPossibleCases(arrays.slice(1)); // recur with the rest of array + const restOfArrays = arrays.slice(1); + const allCombinationsOfRemaining = CoreCombinatorialUtils._getAllCombinations(restOfArrays); // recur with the rest of array // tslint:disable:prefer-for-of - for (let i = 0; i < allCasesOfRest.length; i++) { + for (let i = 0; i < allCombinationsOfRemaining.length; i++) { for (let j = 0; j < arrays[0].length; j++) { - result.push([arrays[0][j], ...allCasesOfRest[i]]); + result.push([arrays[0][j], ...allCombinationsOfRemaining[i]]); } } // tslint:enable:prefer-for-of @@ -283,7 +288,7 @@ export class CoreCombinatorialUtils { balanceAndProxyAllowanceFetcher, ); - // 5. If I fill it by X, what are the resulting balances/allowances/filled amounts exp? + // 5. If I fill it by X, what are the resulting balances/allowances/filled amounts expected? const orderValidationUtils = new OrderValidationUtils(orderFilledCancelledFetcher); const lazyStore = new BalanceAndProxyAllowanceLazyStore(balanceAndProxyAllowanceFetcher); const exchangeTransferSimulator = new ExchangeTransferSimulator(lazyStore); @@ -302,6 +307,7 @@ export class CoreCombinatorialUtils { isFillFailureExpected = true; } + // 6. Fill the order await this._fillOrderAndAssertOutcomeAsync( signedOrder, takerAssetFillAmount, @@ -310,6 +316,216 @@ export class CoreCombinatorialUtils { provider, ); } + private async _fillOrderAndAssertOutcomeAsync( + signedOrder: SignedOrder, + takerAssetFillAmount: BigNumber, + lazyStore: BalanceAndProxyAllowanceLazyStore, + isFillFailureExpected: boolean, + provider: Provider, + ): Promise { + if (isFillFailureExpected) { + return expectRevertOrAlwaysFailingTransactionAsync( + this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { takerAssetFillAmount }), + ); + } + + const makerAddress = signedOrder.makerAddress; + const makerAssetData = signedOrder.makerAssetData; + const takerAssetData = signedOrder.takerAssetData; + const feeRecipient = signedOrder.feeRecipientAddress; + + const expMakerAssetBalanceOfMaker = await lazyStore.getBalanceAsync(makerAssetData, makerAddress); + const expMakerAssetAllowanceOfMaker = await lazyStore.getProxyAllowanceAsync(makerAssetData, makerAddress); + const expTakerAssetBalanceOfMaker = await lazyStore.getBalanceAsync(takerAssetData, makerAddress); + const expZRXAssetBalanceOfMaker = await lazyStore.getBalanceAsync(this.zrxAssetData, makerAddress); + const expZRXAssetAllowanceOfMaker = await lazyStore.getProxyAllowanceAsync(this.zrxAssetData, makerAddress); + const expTakerAssetBalanceOfTaker = await lazyStore.getBalanceAsync(takerAssetData, this.takerAddress); + const expTakerAssetAllowanceOfTaker = await lazyStore.getProxyAllowanceAsync(takerAssetData, this.takerAddress); + const expMakerAssetBalanceOfTaker = await lazyStore.getBalanceAsync(makerAssetData, this.takerAddress); + const expZRXAssetBalanceOfTaker = await lazyStore.getBalanceAsync(this.zrxAssetData, this.takerAddress); + const expZRXAssetAllowanceOfTaker = await lazyStore.getProxyAllowanceAsync( + this.zrxAssetData, + this.takerAddress, + ); + const expZRXAssetBalanceOfFeeRecipient = await lazyStore.getBalanceAsync(this.zrxAssetData, feeRecipient); + + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + const alreadyFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash); + const remainingTakerAmountToFill = signedOrder.takerAssetAmount.minus(alreadyFilledTakerAmount); + const expFilledTakerAmount = takerAssetFillAmount.gt(remainingTakerAmountToFill) + ? remainingTakerAmountToFill + : alreadyFilledTakerAmount.add(takerAssetFillAmount); + + const expFilledMakerAmount = orderUtils.getPartialAmount( + expFilledTakerAmount, + signedOrder.takerAssetAmount, + signedOrder.makerAssetAmount, + ); + + const beforeMakerAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync( + makerAddress, + makerAssetData, + ); + + // - Let's fill the order! + const txReceipt = await this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { + takerAssetFillAmount, + }); + + const actFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash); + expect(actFilledTakerAmount).to.be.bignumber.equal(expFilledTakerAmount, 'filledTakerAmount'); + + expect(txReceipt.logs.length).to.be.equal(1, 'logs length'); + // tslint:disable-next-line:no-unnecessary-type-assertion + const log = txReceipt.logs[0] as LogWithDecodedArgs; + expect(log.args.makerAddress).to.be.equal(makerAddress, 'log.args.makerAddress'); + expect(log.args.takerAddress).to.be.equal(this.takerAddress, 'log.args.this.takerAddress'); + expect(log.args.feeRecipientAddress).to.be.equal(feeRecipient, 'log.args.feeRecipientAddress'); + expect(log.args.makerAssetFilledAmount).to.be.bignumber.equal( + expFilledMakerAmount, + 'log.args.makerAssetFilledAmount', + ); + expect(log.args.takerAssetFilledAmount).to.be.bignumber.equal( + expFilledTakerAmount, + 'log.args.takerAssetFilledAmount', + ); + const expMakerFeePaid = orderUtils.getPartialAmount( + expFilledTakerAmount, + signedOrder.takerAssetAmount, + signedOrder.makerFee, + ); + expect(log.args.makerFeePaid).to.be.bignumber.equal(expMakerFeePaid, 'log.args.makerFeePaid'); + const expTakerFeePaid = orderUtils.getPartialAmount( + expFilledTakerAmount, + signedOrder.takerAssetAmount, + signedOrder.takerFee, + ); + expect(log.args.takerFeePaid).to.be.bignumber.equal(expTakerFeePaid, 'logs.args.takerFeePaid'); + expect(log.args.orderHash).to.be.equal(orderHash, 'log.args.orderHash'); + expect(log.args.makerAssetData).to.be.equal(makerAssetData, 'log.args.makerAssetData'); + expect(log.args.takerAssetData).to.be.equal(takerAssetData, 'log.args.takerAssetData'); + + const actMakerAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, makerAssetData); + expect(actMakerAssetBalanceOfMaker).to.be.bignumber.equal( + expMakerAssetBalanceOfMaker, + 'makerAssetBalanceOfMaker', + ); + + const actMakerAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync( + makerAddress, + makerAssetData, + ); + expect(actMakerAssetAllowanceOfMaker).to.be.bignumber.equal( + expMakerAssetAllowanceOfMaker, + 'makerAssetAllowanceOfMaker', + ); + + const actTakerAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, takerAssetData); + expect(actTakerAssetBalanceOfMaker).to.be.bignumber.equal( + expTakerAssetBalanceOfMaker, + 'takerAssetBalanceOfMaker', + ); + + const actZRXAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, this.zrxAssetData); + expect(actZRXAssetBalanceOfMaker).to.be.bignumber.equal(expZRXAssetBalanceOfMaker, 'ZRXAssetBalanceOfMaker'); + + const actZRXAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync( + makerAddress, + this.zrxAssetData, + ); + expect(actZRXAssetAllowanceOfMaker).to.be.bignumber.equal( + expZRXAssetAllowanceOfMaker, + 'ZRXAssetAllowanceOfMaker', + ); + + const actTakerAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, takerAssetData); + expect(actTakerAssetBalanceOfTaker).to.be.bignumber.equal( + expTakerAssetBalanceOfTaker, + 'TakerAssetBalanceOfTaker', + ); + + const actTakerAssetAllowanceOfTaker = await this.assetWrapper.getProxyAllowanceAsync( + this.takerAddress, + takerAssetData, + ); + + expect(actTakerAssetAllowanceOfTaker).to.be.bignumber.equal( + expTakerAssetAllowanceOfTaker, + 'TakerAssetAllowanceOfTaker', + ); + + const actMakerAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, makerAssetData); + expect(actMakerAssetBalanceOfTaker).to.be.bignumber.equal( + expMakerAssetBalanceOfTaker, + 'MakerAssetBalanceOfTaker', + ); + + const actZRXAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, this.zrxAssetData); + expect(actZRXAssetBalanceOfTaker).to.be.bignumber.equal(expZRXAssetBalanceOfTaker, 'ZRXAssetBalanceOfTaker'); + + const actZRXAssetAllowanceOfTaker = await this.assetWrapper.getProxyAllowanceAsync( + this.takerAddress, + this.zrxAssetData, + ); + expect(actZRXAssetAllowanceOfTaker).to.be.bignumber.equal( + expZRXAssetAllowanceOfTaker, + 'ZRXAssetAllowanceOfTaker', + ); + + const actZRXAssetBalanceOfFeeRecipient = await this.assetWrapper.getBalanceAsync( + feeRecipient, + this.zrxAssetData, + ); + expect(actZRXAssetBalanceOfFeeRecipient).to.be.bignumber.equal( + expZRXAssetBalanceOfFeeRecipient, + 'ZRXAssetBalanceOfFeeRecipient', + ); + } + private async _getTakerAssetFillAmountAsync( + signedOrder: SignedOrder, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario, + balanceAndProxyAllowanceFetcher: SimpleAssetBalanceAndProxyAllowanceFetcher, + orderFilledCancelledFetcher: SimpleOrderFilledCancelledFetcher, + ): Promise { + const orderStateUtils = new OrderStateUtils(balanceAndProxyAllowanceFetcher, orderFilledCancelledFetcher); + const fillableTakerAssetAmount = await orderStateUtils.getMaxFillableTakerAssetAmountAsync( + signedOrder, + this.takerAddress, + ); + + let takerAssetFillAmount; + switch (takerAssetFillAmountScenario) { + case TakerAssetFillAmountScenario.Zero: + takerAssetFillAmount = new BigNumber(0); + break; + + case TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount: + takerAssetFillAmount = fillableTakerAssetAmount; + break; + + case TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount: + takerAssetFillAmount = fillableTakerAssetAmount.add(1); + break; + + case TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount: + const takerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.takerAssetData); + const makerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.makerAssetData); + const isEitherAssetERC721 = + takerAssetProxyId === constants.ERC721_PROXY_ID || makerAssetProxyId === constants.ERC721_PROXY_ID; + if (isEitherAssetERC721) { + throw new Error( + 'Cannot test `TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount` together with ERC721 assets since orders involving ERC721 must always be filled exactly.', + ); + } + takerAssetFillAmount = fillableTakerAssetAmount.div(2).floor(); + break; + + default: + throw errorUtils.spawnSwitchErr('TakerAssetFillAmountScenario', takerAssetFillAmountScenario); + } + + return takerAssetFillAmount; + } private async _modifyTraderStateAsync( makerStateScenario: TraderStateScenario, takerStateScenario: TraderStateScenario, @@ -546,214 +762,4 @@ export class CoreCombinatorialUtils { ); } } - private async _getTakerAssetFillAmountAsync( - signedOrder: SignedOrder, - takerAssetFillAmountScenario: TakerAssetFillAmountScenario, - balanceAndProxyAllowanceFetcher: SimpleAssetBalanceAndProxyAllowanceFetcher, - orderFilledCancelledFetcher: SimpleOrderFilledCancelledFetcher, - ): Promise { - const orderStateUtils = new OrderStateUtils(balanceAndProxyAllowanceFetcher, orderFilledCancelledFetcher); - const fillableTakerAssetAmount = await orderStateUtils.getMaxFillableTakerAssetAmountAsync( - signedOrder, - this.takerAddress, - ); - - let takerAssetFillAmount; - switch (takerAssetFillAmountScenario) { - case TakerAssetFillAmountScenario.Zero: - takerAssetFillAmount = new BigNumber(0); - break; - - case TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount: - takerAssetFillAmount = fillableTakerAssetAmount; - break; - - case TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount: - takerAssetFillAmount = fillableTakerAssetAmount.add(1); - break; - - case TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount: - const takerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.takerAssetData); - const makerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.makerAssetData); - const isEitherAssetERC721 = - takerAssetProxyId === constants.ERC721_PROXY_ID || makerAssetProxyId === constants.ERC721_PROXY_ID; - if (isEitherAssetERC721) { - throw new Error( - 'Cannot test `TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount` together with ERC721 assets since orders involving ERC721 must always be filled exactly.', - ); - } - takerAssetFillAmount = fillableTakerAssetAmount.div(2).floor(); - break; - - default: - throw errorUtils.spawnSwitchErr('TakerAssetFillAmountScenario', takerAssetFillAmountScenario); - } - - return takerAssetFillAmount; - } - private async _fillOrderAndAssertOutcomeAsync( - signedOrder: SignedOrder, - takerAssetFillAmount: BigNumber, - lazyStore: BalanceAndProxyAllowanceLazyStore, - isFillFailureExpected: boolean, - provider: Provider, - ): Promise { - if (isFillFailureExpected) { - return expectRevertOrAlwaysFailingTransactionAsync( - this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { takerAssetFillAmount }), - ); - } - - const makerAddress = signedOrder.makerAddress; - const makerAssetData = signedOrder.makerAssetData; - const takerAssetData = signedOrder.takerAssetData; - const feeRecipient = signedOrder.feeRecipientAddress; - - const expMakerAssetBalanceOfMaker = await lazyStore.getBalanceAsync(makerAssetData, makerAddress); - const expMakerAssetAllowanceOfMaker = await lazyStore.getProxyAllowanceAsync(makerAssetData, makerAddress); - const expTakerAssetBalanceOfMaker = await lazyStore.getBalanceAsync(takerAssetData, makerAddress); - const expZRXAssetBalanceOfMaker = await lazyStore.getBalanceAsync(this.zrxAssetData, makerAddress); - const expZRXAssetAllowanceOfMaker = await lazyStore.getProxyAllowanceAsync(this.zrxAssetData, makerAddress); - const expTakerAssetBalanceOfTaker = await lazyStore.getBalanceAsync(takerAssetData, this.takerAddress); - const expTakerAssetAllowanceOfTaker = await lazyStore.getProxyAllowanceAsync(takerAssetData, this.takerAddress); - const expMakerAssetBalanceOfTaker = await lazyStore.getBalanceAsync(makerAssetData, this.takerAddress); - const expZRXAssetBalanceOfTaker = await lazyStore.getBalanceAsync(this.zrxAssetData, this.takerAddress); - const expZRXAssetAllowanceOfTaker = await lazyStore.getProxyAllowanceAsync( - this.zrxAssetData, - this.takerAddress, - ); - const expZRXAssetBalanceOfFeeRecipient = await lazyStore.getBalanceAsync(this.zrxAssetData, feeRecipient); - - const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - const alreadyFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash); - const remainingTakerAmountToFill = signedOrder.takerAssetAmount.minus(alreadyFilledTakerAmount); - const expFilledTakerAmount = takerAssetFillAmount.gt(remainingTakerAmountToFill) - ? remainingTakerAmountToFill - : alreadyFilledTakerAmount.add(takerAssetFillAmount); - - const expFilledMakerAmount = orderUtils.getPartialAmount( - expFilledTakerAmount, - signedOrder.takerAssetAmount, - signedOrder.makerAssetAmount, - ); - - const beforeMakerAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync( - makerAddress, - makerAssetData, - ); - - // - Let's fill the order! - const txReceipt = await this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { - takerAssetFillAmount, - }); - - const actFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash); - expect(actFilledTakerAmount).to.be.bignumber.equal(expFilledTakerAmount, 'filledTakerAmount'); - - expect(txReceipt.logs.length).to.be.equal(1, 'logs length'); - // tslint:disable-next-line:no-unnecessary-type-assertion - const log = txReceipt.logs[0] as LogWithDecodedArgs; - expect(log.args.makerAddress).to.be.equal(makerAddress, 'log.args.makerAddress'); - expect(log.args.takerAddress).to.be.equal(this.takerAddress, 'log.args.this.takerAddress'); - expect(log.args.feeRecipientAddress).to.be.equal(feeRecipient, 'log.args.feeRecipientAddress'); - expect(log.args.makerAssetFilledAmount).to.be.bignumber.equal( - expFilledMakerAmount, - 'log.args.makerAssetFilledAmount', - ); - expect(log.args.takerAssetFilledAmount).to.be.bignumber.equal( - expFilledTakerAmount, - 'log.args.takerAssetFilledAmount', - ); - const expMakerFeePaid = orderUtils.getPartialAmount( - expFilledTakerAmount, - signedOrder.takerAssetAmount, - signedOrder.makerFee, - ); - expect(log.args.makerFeePaid).to.be.bignumber.equal(expMakerFeePaid, 'log.args.makerFeePaid'); - const expTakerFeePaid = orderUtils.getPartialAmount( - expFilledTakerAmount, - signedOrder.takerAssetAmount, - signedOrder.takerFee, - ); - expect(log.args.takerFeePaid).to.be.bignumber.equal(expTakerFeePaid, 'logs.args.takerFeePaid'); - expect(log.args.orderHash).to.be.equal(orderHash, 'log.args.orderHash'); - expect(log.args.makerAssetData).to.be.equal(makerAssetData, 'log.args.makerAssetData'); - expect(log.args.takerAssetData).to.be.equal(takerAssetData, 'log.args.takerAssetData'); - - const actMakerAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, makerAssetData); - expect(actMakerAssetBalanceOfMaker).to.be.bignumber.equal( - expMakerAssetBalanceOfMaker, - 'makerAssetBalanceOfMaker', - ); - - const actMakerAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync( - makerAddress, - makerAssetData, - ); - expect(actMakerAssetAllowanceOfMaker).to.be.bignumber.equal( - expMakerAssetAllowanceOfMaker, - 'makerAssetAllowanceOfMaker', - ); - - const actTakerAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, takerAssetData); - expect(actTakerAssetBalanceOfMaker).to.be.bignumber.equal( - expTakerAssetBalanceOfMaker, - 'takerAssetBalanceOfMaker', - ); - - const actZRXAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, this.zrxAssetData); - expect(actZRXAssetBalanceOfMaker).to.be.bignumber.equal(expZRXAssetBalanceOfMaker, 'ZRXAssetBalanceOfMaker'); - - const actZRXAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync( - makerAddress, - this.zrxAssetData, - ); - expect(actZRXAssetAllowanceOfMaker).to.be.bignumber.equal( - expZRXAssetAllowanceOfMaker, - 'ZRXAssetAllowanceOfMaker', - ); - - const actTakerAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, takerAssetData); - expect(actTakerAssetBalanceOfTaker).to.be.bignumber.equal( - expTakerAssetBalanceOfTaker, - 'TakerAssetBalanceOfTaker', - ); - - const actTakerAssetAllowanceOfTaker = await this.assetWrapper.getProxyAllowanceAsync( - this.takerAddress, - takerAssetData, - ); - - expect(actTakerAssetAllowanceOfTaker).to.be.bignumber.equal( - expTakerAssetAllowanceOfTaker, - 'TakerAssetAllowanceOfTaker', - ); - - const actMakerAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, makerAssetData); - expect(actMakerAssetBalanceOfTaker).to.be.bignumber.equal( - expMakerAssetBalanceOfTaker, - 'MakerAssetBalanceOfTaker', - ); - - const actZRXAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, this.zrxAssetData); - expect(actZRXAssetBalanceOfTaker).to.be.bignumber.equal(expZRXAssetBalanceOfTaker, 'ZRXAssetBalanceOfTaker'); - - const actZRXAssetAllowanceOfTaker = await this.assetWrapper.getProxyAllowanceAsync( - this.takerAddress, - this.zrxAssetData, - ); - expect(actZRXAssetAllowanceOfTaker).to.be.bignumber.equal( - expZRXAssetAllowanceOfTaker, - 'ZRXAssetAllowanceOfTaker', - ); - - const actZRXAssetBalanceOfFeeRecipient = await this.assetWrapper.getBalanceAsync( - feeRecipient, - this.zrxAssetData, - ); - expect(actZRXAssetBalanceOfFeeRecipient).to.be.bignumber.equal( - expZRXAssetBalanceOfFeeRecipient, - 'ZRXAssetBalanceOfFeeRecipient', - ); - } -} +} // tslint:disable:max-file-line-count -- cgit From 9acf4458c026ca9e2748ca4291573ac95c66fe26 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 19 Jun 2018 17:38:15 +0200 Subject: Remove hack in ERC20Wrapper --- packages/contracts/src/utils/erc20_wrapper.ts | 12 ++++++------ packages/contracts/test/asset_proxy/proxies.ts | 5 ++++- packages/contracts/test/exchange/core.ts | 5 ++++- packages/contracts/test/exchange/dispatcher.ts | 5 ++++- packages/contracts/test/exchange/match_orders.ts | 5 ++++- packages/contracts/test/exchange/transactions.ts | 5 ++++- packages/contracts/test/exchange/wrapper.ts | 5 ++++- 7 files changed, 30 insertions(+), 12 deletions(-) diff --git a/packages/contracts/src/utils/erc20_wrapper.ts b/packages/contracts/src/utils/erc20_wrapper.ts index ca2ca492b..6dd717f4a 100644 --- a/packages/contracts/src/utils/erc20_wrapper.ts +++ b/packages/contracts/src/utils/erc20_wrapper.ts @@ -27,11 +27,11 @@ export class ERC20Wrapper { this._tokenOwnerAddresses = tokenOwnerAddresses; this._contractOwnerAddress = contractOwnerAddress; } - public async deployDummyTokensAsync(num?: number, decimals?: BigNumber): Promise { - // TODO(fabio): Remove and refactor all tests - const finalNum = _.isUndefined(num) ? constants.NUM_DUMMY_ERC20_TO_DEPLOY : num; - const finalDecimals = _.isUndefined(decimals) ? constants.DUMMY_TOKEN_DECIMALS : decimals; - for (let i = 0; i < finalNum; i++) { + public async deployDummyTokensAsync( + numberToDeploy: number, + decimals: BigNumber, + ): Promise { + for (let i = 0; i < numberToDeploy; i++) { this._dummyTokenContracts.push( await DummyERC20TokenContract.deployFrom0xArtifactAsync( artifacts.DummyERC20Token, @@ -39,7 +39,7 @@ export class ERC20Wrapper { txDefaults, constants.DUMMY_TOKEN_NAME, constants.DUMMY_TOKEN_SYMBOL, - finalDecimals, + decimals, constants.DUMMY_TOKEN_TOTAL_SUPPLY, ), ); diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts index 4a51e0db8..185d1639e 100644 --- a/packages/contracts/test/asset_proxy/proxies.ts +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -58,7 +58,10 @@ describe('Asset Transfer Proxies', () => { erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); - [zrxToken] = await erc20Wrapper.deployDummyTokensAsync(); + [zrxToken] = await erc20Wrapper.deployDummyTokensAsync( + constants.NUM_DUMMY_ERC20_TO_DEPLOY, + constants.DUMMY_TOKEN_DECIMALS, + ); erc20Proxy = await erc20Wrapper.deployProxyAsync(); await erc20Wrapper.setBalancesAndAllowancesAsync(); await web3Wrapper.awaitTransactionSuccessAsync( diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index 453f05df2..64b7bf943 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -72,7 +72,10 @@ describe('Exchange core', () => { erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); - [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(); + [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( + constants.NUM_DUMMY_ERC20_TO_DEPLOY, + constants.DUMMY_TOKEN_DECIMALS, + ); erc20Proxy = await erc20Wrapper.deployProxyAsync(); await erc20Wrapper.setBalancesAndAllowancesAsync(); diff --git a/packages/contracts/test/exchange/dispatcher.ts b/packages/contracts/test/exchange/dispatcher.ts index abbfd7ac7..0e2b82f19 100644 --- a/packages/contracts/test/exchange/dispatcher.ts +++ b/packages/contracts/test/exchange/dispatcher.ts @@ -48,7 +48,10 @@ describe('AssetProxyDispatcher', () => { erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); - [zrxToken] = await erc20Wrapper.deployDummyTokensAsync(); + [zrxToken] = await erc20Wrapper.deployDummyTokensAsync( + constants.NUM_DUMMY_ERC20_TO_DEPLOY, + constants.DUMMY_TOKEN_DECIMALS, + ); erc20Proxy = await erc20Wrapper.deployProxyAsync(); await erc20Wrapper.setBalancesAndAllowancesAsync(); diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index b8dca04fd..4ed4f2266 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -81,7 +81,10 @@ describe('matchOrders', () => { erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); // Deploy ERC20 token & ERC20 proxy - [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(); + [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( + constants.NUM_DUMMY_ERC20_TO_DEPLOY, + constants.DUMMY_TOKEN_DECIMALS, + ); erc20Proxy = await erc20Wrapper.deployProxyAsync(); await erc20Wrapper.setBalancesAndAllowancesAsync(); // Deploy ERC721 token and proxy diff --git a/packages/contracts/test/exchange/transactions.ts b/packages/contracts/test/exchange/transactions.ts index 21ff123e5..beb7054e7 100644 --- a/packages/contracts/test/exchange/transactions.ts +++ b/packages/contracts/test/exchange/transactions.ts @@ -64,7 +64,10 @@ describe('Exchange transactions', () => { erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); - [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(); + [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( + constants.NUM_DUMMY_ERC20_TO_DEPLOY, + constants.DUMMY_TOKEN_DECIMALS, + ); erc20Proxy = await erc20Wrapper.deployProxyAsync(); await erc20Wrapper.setBalancesAndAllowancesAsync(); diff --git a/packages/contracts/test/exchange/wrapper.ts b/packages/contracts/test/exchange/wrapper.ts index 00c5e4352..6f03c7f6f 100644 --- a/packages/contracts/test/exchange/wrapper.ts +++ b/packages/contracts/test/exchange/wrapper.ts @@ -66,7 +66,10 @@ describe('Exchange wrappers', () => { erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); - [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(); + [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( + constants.NUM_DUMMY_ERC20_TO_DEPLOY, + constants.DUMMY_TOKEN_DECIMALS, + ); erc20Proxy = await erc20Wrapper.deployProxyAsync(); await erc20Wrapper.setBalancesAndAllowancesAsync(); -- cgit From c0e008c601d4c34da6faafce62abfd8e0ff821c5 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 19 Jun 2018 17:41:10 +0200 Subject: Rename NewOrderFactory to OrderFactoryFromScenario for clarity --- .../src/utils/core_combinatorial_utils.ts | 8 +- packages/contracts/src/utils/new_order_factory.ts | 277 --------------------- .../src/utils/order_factory_from_scenario.ts | 277 +++++++++++++++++++++ 3 files changed, 281 insertions(+), 281 deletions(-) delete mode 100644 packages/contracts/src/utils/new_order_factory.ts create mode 100644 packages/contracts/src/utils/order_factory_from_scenario.ts diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index d6142c5fb..cdb8e579d 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -23,7 +23,7 @@ import { constants } from '../utils/constants'; import { ERC20Wrapper } from '../utils/erc20_wrapper'; import { ERC721Wrapper } from '../utils/erc721_wrapper'; import { ExchangeWrapper } from '../utils/exchange_wrapper'; -import { NewOrderFactory } from '../utils/new_order_factory'; +import { OrderFactoryFromScenario } from '../utils/order_factory_from_scenario'; import { orderUtils } from '../utils/order_utils'; import { signingUtils } from '../utils/signing_utils'; import { SimpleAssetBalanceAndProxyAllowanceFetcher } from '../utils/simple_asset_balance_and_proxy_allowance_fetcher'; @@ -111,7 +111,7 @@ export async function coreCombinatorialUtilsFactoryAsync( constants.AWAIT_TRANSACTION_MINED_MS, ); - const orderFactory = new NewOrderFactory( + const orderFactory = new OrderFactoryFromScenario( userAddresses, zrxToken.address, [erc20EighteenDecimalTokenA.address, erc20EighteenDecimalTokenB.address], @@ -135,7 +135,7 @@ export async function coreCombinatorialUtilsFactoryAsync( } export class CoreCombinatorialUtils { - public orderFactory: NewOrderFactory; + public orderFactory: OrderFactoryFromScenario; public ownerAddress: string; public makerAddress: string; public makerPrivateKey: Buffer; @@ -235,7 +235,7 @@ export class CoreCombinatorialUtils { } } constructor( - orderFactory: NewOrderFactory, + orderFactory: OrderFactoryFromScenario, ownerAddress: string, makerAddress: string, makerPrivateKey: Buffer, diff --git a/packages/contracts/src/utils/new_order_factory.ts b/packages/contracts/src/utils/new_order_factory.ts deleted file mode 100644 index 22705d3e7..000000000 --- a/packages/contracts/src/utils/new_order_factory.ts +++ /dev/null @@ -1,277 +0,0 @@ -import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; -import { Order } from '@0xproject/types'; -import { BigNumber, errorUtils } from '@0xproject/utils'; - -import { DummyERC721TokenContract } from '../generated_contract_wrappers/dummy_e_r_c721_token'; - -import { constants } from './constants'; -import { - AssetDataScenario, - ERC721TokenIdsByOwner, - ExpirationTimeSecondsScenario, - FeeRecipientAddressScenario, - OrderAssetAmountScenario, - OrderScenario, - TakerScenario, -} from './types'; - -const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10000000000000000000); -const FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(5000000000000000000); -const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100000000000000000); -const POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(50000000000000000); -const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1000000); -const FIVE_UNITS_FIVE_DECIMALS = new BigNumber(500000); -const ONE_NFT_UNIT = new BigNumber(1); - -export class NewOrderFactory { - private _userAddresses: string[]; - private _zrxAddress: string; - private _nonZrxERC20EighteenDecimalTokenAddresses: string[]; - private _erc20FiveDecimalTokenAddresses: string[]; - private _erc721Token: DummyERC721TokenContract; - private _erc721Balances: ERC721TokenIdsByOwner; - private _exchangeAddress: string; - constructor( - userAddresses: string[], - zrxAddress: string, - nonZrxERC20EighteenDecimalTokenAddresses: string[], - erc20FiveDecimalTokenAddresses: string[], - erc721Token: DummyERC721TokenContract, - erc721Balances: ERC721TokenIdsByOwner, - exchangeAddress: string, - ) { - this._userAddresses = userAddresses; - this._zrxAddress = zrxAddress; - this._nonZrxERC20EighteenDecimalTokenAddresses = nonZrxERC20EighteenDecimalTokenAddresses; - this._erc20FiveDecimalTokenAddresses = erc20FiveDecimalTokenAddresses; - this._erc721Token = erc721Token; - this._erc721Balances = erc721Balances; - this._exchangeAddress = exchangeAddress; - } - public generateOrder(orderScenario: OrderScenario): Order { - const makerAddress = this._userAddresses[1]; - let takerAddress = this._userAddresses[2]; - const erc721MakerAssetIds = this._erc721Balances[makerAddress][this._erc721Token.address]; - const erc721TakerAssetIds = this._erc721Balances[takerAddress][this._erc721Token.address]; - let feeRecipientAddress; - let makerAssetAmount; - let takerAssetAmount; - let makerFee; - let takerFee; - let expirationTimeSeconds; - let makerAssetData; - let takerAssetData; - - switch (orderScenario.feeRecipientScenario) { - case FeeRecipientAddressScenario.BurnAddress: - feeRecipientAddress = constants.NULL_ADDRESS; - break; - case FeeRecipientAddressScenario.EthUserAddress: - feeRecipientAddress = this._userAddresses[4]; - break; - default: - throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', orderScenario.feeRecipientScenario); - } - - switch (orderScenario.makerAssetDataScenario) { - case AssetDataScenario.ZRXFeeToken: - makerAssetData = assetProxyUtils.encodeERC20AssetData(this._zrxAddress); - break; - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - makerAssetData = assetProxyUtils.encodeERC20AssetData( - this._nonZrxERC20EighteenDecimalTokenAddresses[0], - ); - break; - case AssetDataScenario.ERC20FiveDecimals: - makerAssetData = assetProxyUtils.encodeERC20AssetData(this._erc20FiveDecimalTokenAddresses[0]); - break; - case AssetDataScenario.ERC721: - makerAssetData = assetProxyUtils.encodeERC721AssetData( - this._erc721Token.address, - erc721MakerAssetIds[0], - ); - break; - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); - } - - switch (orderScenario.takerAssetDataScenario) { - case AssetDataScenario.ZRXFeeToken: - takerAssetData = assetProxyUtils.encodeERC20AssetData(this._zrxAddress); - break; - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - takerAssetData = assetProxyUtils.encodeERC20AssetData( - this._nonZrxERC20EighteenDecimalTokenAddresses[1], - ); - break; - case AssetDataScenario.ERC20FiveDecimals: - takerAssetData = assetProxyUtils.encodeERC20AssetData(this._erc20FiveDecimalTokenAddresses[1]); - break; - case AssetDataScenario.ERC721: - takerAssetData = assetProxyUtils.encodeERC721AssetData( - this._erc721Token.address, - erc721TakerAssetIds[0], - ); - break; - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); - } - - switch (orderScenario.makerAssetAmountScenario) { - case OrderAssetAmountScenario.Large: - switch (orderScenario.makerAssetDataScenario) { - case AssetDataScenario.ZRXFeeToken: - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - makerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; - break; - case AssetDataScenario.ERC20FiveDecimals: - makerAssetAmount = TEN_UNITS_FIVE_DECIMALS; - break; - case AssetDataScenario.ERC721: - makerAssetAmount = ONE_NFT_UNIT; - break; - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); - } - break; - case OrderAssetAmountScenario.Small: - switch (orderScenario.makerAssetDataScenario) { - case AssetDataScenario.ZRXFeeToken: - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - makerAssetAmount = FIVE_UNITS_EIGHTEEN_DECIMALS; - break; - case AssetDataScenario.ERC20FiveDecimals: - makerAssetAmount = FIVE_UNITS_FIVE_DECIMALS; - break; - case AssetDataScenario.ERC721: - makerAssetAmount = ONE_NFT_UNIT; - break; - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); - } - break; - case OrderAssetAmountScenario.Zero: - makerAssetAmount = new BigNumber(0); - break; - default: - throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.makerAssetAmountScenario); - } - - switch (orderScenario.takerAssetAmountScenario) { - case OrderAssetAmountScenario.Large: - switch (orderScenario.takerAssetDataScenario) { - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - case AssetDataScenario.ZRXFeeToken: - takerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; - break; - case AssetDataScenario.ERC20FiveDecimals: - takerAssetAmount = TEN_UNITS_FIVE_DECIMALS; - break; - case AssetDataScenario.ERC721: - takerAssetAmount = ONE_NFT_UNIT; - break; - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); - } - break; - case OrderAssetAmountScenario.Small: - switch (orderScenario.takerAssetDataScenario) { - case AssetDataScenario.ERC20NonZRXEighteenDecimals: - case AssetDataScenario.ZRXFeeToken: - takerAssetAmount = FIVE_UNITS_EIGHTEEN_DECIMALS; - break; - case AssetDataScenario.ERC20FiveDecimals: - takerAssetAmount = FIVE_UNITS_FIVE_DECIMALS; - break; - case AssetDataScenario.ERC721: - takerAssetAmount = ONE_NFT_UNIT; - break; - default: - throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); - } - break; - case OrderAssetAmountScenario.Zero: - takerAssetAmount = new BigNumber(0); - break; - default: - throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.takerAssetAmountScenario); - } - - switch (orderScenario.makerFeeScenario) { - case OrderAssetAmountScenario.Large: - makerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; - break; - case OrderAssetAmountScenario.Small: - makerFee = POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS; - break; - case OrderAssetAmountScenario.Zero: - makerFee = new BigNumber(0); - break; - default: - throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.makerFeeScenario); - } - - switch (orderScenario.takerFeeScenario) { - case OrderAssetAmountScenario.Large: - takerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; - break; - case OrderAssetAmountScenario.Small: - takerFee = POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS; - break; - case OrderAssetAmountScenario.Zero: - takerFee = new BigNumber(0); - break; - default: - throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.takerFeeScenario); - } - - switch (orderScenario.expirationTimeSecondsScenario) { - case ExpirationTimeSecondsScenario.InFuture: - expirationTimeSeconds = new BigNumber(2524604400); // Close to infinite - break; - case ExpirationTimeSecondsScenario.InPast: - expirationTimeSeconds = new BigNumber(0); // Jan 1, 1970 - break; - default: - throw errorUtils.spawnSwitchErr( - 'ExpirationTimeSecondsScenario', - orderScenario.expirationTimeSecondsScenario, - ); - } - - switch (orderScenario.takerScenario) { - case TakerScenario.CorrectlySpecified: - break; // noop since takerAddress is already specified - - case TakerScenario.IncorrectlySpecified: - const notTaker = this._userAddresses[3]; - takerAddress = notTaker; - break; - - case TakerScenario.Unspecified: - takerAddress = constants.NULL_ADDRESS; - break; - - default: - throw errorUtils.spawnSwitchErr('TakerScenario', orderScenario.takerScenario); - } - - const order: Order = { - senderAddress: constants.NULL_ADDRESS, - makerAddress, - takerAddress, - makerFee, - takerFee, - makerAssetAmount, - takerAssetAmount, - makerAssetData, - takerAssetData, - salt: generatePseudoRandomSalt(), - exchangeAddress: this._exchangeAddress, - feeRecipientAddress, - expirationTimeSeconds, - }; - - return order; - } -} diff --git a/packages/contracts/src/utils/order_factory_from_scenario.ts b/packages/contracts/src/utils/order_factory_from_scenario.ts new file mode 100644 index 000000000..74c2d4fa2 --- /dev/null +++ b/packages/contracts/src/utils/order_factory_from_scenario.ts @@ -0,0 +1,277 @@ +import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { Order } from '@0xproject/types'; +import { BigNumber, errorUtils } from '@0xproject/utils'; + +import { DummyERC721TokenContract } from '../generated_contract_wrappers/dummy_e_r_c721_token'; + +import { constants } from './constants'; +import { + AssetDataScenario, + ERC721TokenIdsByOwner, + ExpirationTimeSecondsScenario, + FeeRecipientAddressScenario, + OrderAssetAmountScenario, + OrderScenario, + TakerScenario, +} from './types'; + +const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10000000000000000000); +const FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(5000000000000000000); +const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100000000000000000); +const POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(50000000000000000); +const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1000000); +const FIVE_UNITS_FIVE_DECIMALS = new BigNumber(500000); +const ONE_NFT_UNIT = new BigNumber(1); + +export class OrderFactoryFromScenario { + private _userAddresses: string[]; + private _zrxAddress: string; + private _nonZrxERC20EighteenDecimalTokenAddresses: string[]; + private _erc20FiveDecimalTokenAddresses: string[]; + private _erc721Token: DummyERC721TokenContract; + private _erc721Balances: ERC721TokenIdsByOwner; + private _exchangeAddress: string; + constructor( + userAddresses: string[], + zrxAddress: string, + nonZrxERC20EighteenDecimalTokenAddresses: string[], + erc20FiveDecimalTokenAddresses: string[], + erc721Token: DummyERC721TokenContract, + erc721Balances: ERC721TokenIdsByOwner, + exchangeAddress: string, + ) { + this._userAddresses = userAddresses; + this._zrxAddress = zrxAddress; + this._nonZrxERC20EighteenDecimalTokenAddresses = nonZrxERC20EighteenDecimalTokenAddresses; + this._erc20FiveDecimalTokenAddresses = erc20FiveDecimalTokenAddresses; + this._erc721Token = erc721Token; + this._erc721Balances = erc721Balances; + this._exchangeAddress = exchangeAddress; + } + public generateOrder(orderScenario: OrderScenario): Order { + const makerAddress = this._userAddresses[1]; + let takerAddress = this._userAddresses[2]; + const erc721MakerAssetIds = this._erc721Balances[makerAddress][this._erc721Token.address]; + const erc721TakerAssetIds = this._erc721Balances[takerAddress][this._erc721Token.address]; + let feeRecipientAddress; + let makerAssetAmount; + let takerAssetAmount; + let makerFee; + let takerFee; + let expirationTimeSeconds; + let makerAssetData; + let takerAssetData; + + switch (orderScenario.feeRecipientScenario) { + case FeeRecipientAddressScenario.BurnAddress: + feeRecipientAddress = constants.NULL_ADDRESS; + break; + case FeeRecipientAddressScenario.EthUserAddress: + feeRecipientAddress = this._userAddresses[4]; + break; + default: + throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', orderScenario.feeRecipientScenario); + } + + switch (orderScenario.makerAssetDataScenario) { + case AssetDataScenario.ZRXFeeToken: + makerAssetData = assetProxyUtils.encodeERC20AssetData(this._zrxAddress); + break; + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + makerAssetData = assetProxyUtils.encodeERC20AssetData( + this._nonZrxERC20EighteenDecimalTokenAddresses[0], + ); + break; + case AssetDataScenario.ERC20FiveDecimals: + makerAssetData = assetProxyUtils.encodeERC20AssetData(this._erc20FiveDecimalTokenAddresses[0]); + break; + case AssetDataScenario.ERC721: + makerAssetData = assetProxyUtils.encodeERC721AssetData( + this._erc721Token.address, + erc721MakerAssetIds[0], + ); + break; + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); + } + + switch (orderScenario.takerAssetDataScenario) { + case AssetDataScenario.ZRXFeeToken: + takerAssetData = assetProxyUtils.encodeERC20AssetData(this._zrxAddress); + break; + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + takerAssetData = assetProxyUtils.encodeERC20AssetData( + this._nonZrxERC20EighteenDecimalTokenAddresses[1], + ); + break; + case AssetDataScenario.ERC20FiveDecimals: + takerAssetData = assetProxyUtils.encodeERC20AssetData(this._erc20FiveDecimalTokenAddresses[1]); + break; + case AssetDataScenario.ERC721: + takerAssetData = assetProxyUtils.encodeERC721AssetData( + this._erc721Token.address, + erc721TakerAssetIds[0], + ); + break; + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); + } + + switch (orderScenario.makerAssetAmountScenario) { + case OrderAssetAmountScenario.Large: + switch (orderScenario.makerAssetDataScenario) { + case AssetDataScenario.ZRXFeeToken: + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + makerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; + break; + case AssetDataScenario.ERC20FiveDecimals: + makerAssetAmount = TEN_UNITS_FIVE_DECIMALS; + break; + case AssetDataScenario.ERC721: + makerAssetAmount = ONE_NFT_UNIT; + break; + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); + } + break; + case OrderAssetAmountScenario.Small: + switch (orderScenario.makerAssetDataScenario) { + case AssetDataScenario.ZRXFeeToken: + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + makerAssetAmount = FIVE_UNITS_EIGHTEEN_DECIMALS; + break; + case AssetDataScenario.ERC20FiveDecimals: + makerAssetAmount = FIVE_UNITS_FIVE_DECIMALS; + break; + case AssetDataScenario.ERC721: + makerAssetAmount = ONE_NFT_UNIT; + break; + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario); + } + break; + case OrderAssetAmountScenario.Zero: + makerAssetAmount = new BigNumber(0); + break; + default: + throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.makerAssetAmountScenario); + } + + switch (orderScenario.takerAssetAmountScenario) { + case OrderAssetAmountScenario.Large: + switch (orderScenario.takerAssetDataScenario) { + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + case AssetDataScenario.ZRXFeeToken: + takerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; + break; + case AssetDataScenario.ERC20FiveDecimals: + takerAssetAmount = TEN_UNITS_FIVE_DECIMALS; + break; + case AssetDataScenario.ERC721: + takerAssetAmount = ONE_NFT_UNIT; + break; + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); + } + break; + case OrderAssetAmountScenario.Small: + switch (orderScenario.takerAssetDataScenario) { + case AssetDataScenario.ERC20NonZRXEighteenDecimals: + case AssetDataScenario.ZRXFeeToken: + takerAssetAmount = FIVE_UNITS_EIGHTEEN_DECIMALS; + break; + case AssetDataScenario.ERC20FiveDecimals: + takerAssetAmount = FIVE_UNITS_FIVE_DECIMALS; + break; + case AssetDataScenario.ERC721: + takerAssetAmount = ONE_NFT_UNIT; + break; + default: + throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario); + } + break; + case OrderAssetAmountScenario.Zero: + takerAssetAmount = new BigNumber(0); + break; + default: + throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.takerAssetAmountScenario); + } + + switch (orderScenario.makerFeeScenario) { + case OrderAssetAmountScenario.Large: + makerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; + break; + case OrderAssetAmountScenario.Small: + makerFee = POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS; + break; + case OrderAssetAmountScenario.Zero: + makerFee = new BigNumber(0); + break; + default: + throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.makerFeeScenario); + } + + switch (orderScenario.takerFeeScenario) { + case OrderAssetAmountScenario.Large: + takerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; + break; + case OrderAssetAmountScenario.Small: + takerFee = POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS; + break; + case OrderAssetAmountScenario.Zero: + takerFee = new BigNumber(0); + break; + default: + throw errorUtils.spawnSwitchErr('OrderAssetAmountScenario', orderScenario.takerFeeScenario); + } + + switch (orderScenario.expirationTimeSecondsScenario) { + case ExpirationTimeSecondsScenario.InFuture: + expirationTimeSeconds = new BigNumber(2524604400); // Close to infinite + break; + case ExpirationTimeSecondsScenario.InPast: + expirationTimeSeconds = new BigNumber(0); // Jan 1, 1970 + break; + default: + throw errorUtils.spawnSwitchErr( + 'ExpirationTimeSecondsScenario', + orderScenario.expirationTimeSecondsScenario, + ); + } + + switch (orderScenario.takerScenario) { + case TakerScenario.CorrectlySpecified: + break; // noop since takerAddress is already specified + + case TakerScenario.IncorrectlySpecified: + const notTaker = this._userAddresses[3]; + takerAddress = notTaker; + break; + + case TakerScenario.Unspecified: + takerAddress = constants.NULL_ADDRESS; + break; + + default: + throw errorUtils.spawnSwitchErr('TakerScenario', orderScenario.takerScenario); + } + + const order: Order = { + senderAddress: constants.NULL_ADDRESS, + makerAddress, + takerAddress, + makerFee, + takerFee, + makerAssetAmount, + takerAssetAmount, + makerAssetData, + takerAssetData, + salt: generatePseudoRandomSalt(), + exchangeAddress: this._exchangeAddress, + feeRecipientAddress, + expirationTimeSeconds, + }; + + return order; + } +} -- cgit From f35af1fb6d141d23506d418a4110752595ff0b3d Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 19 Jun 2018 17:53:16 +0200 Subject: Fix typo --- packages/contracts/src/utils/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index d71c14495..5be65fb1d 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -165,8 +165,8 @@ export enum OrderAssetAmountScenario { } export enum TakerScenario { - CorrectlySpecified = 'CORRECTLY_SPECFIED', - IncorrectlySpecified = 'INCORRECTLY_SPECFIED', + CorrectlySpecified = 'CORRECTLY_SPECIFIED', + IncorrectlySpecified = 'INCORRECTLY_SPECIFIED', Unspecified = 'UNSPECIFIED', } -- cgit From 5541327968ad6f974a37c49057253746f738a709 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 20 Jun 2018 13:24:09 +0200 Subject: Add ability for verbose logging --- packages/contracts/src/utils/core_combinatorial_utils.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index cdb8e579d..cf87ad36f 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -7,7 +7,7 @@ import { OrderValidationUtils, } from '@0xproject/order-utils'; import { AssetProxyId, Order, SignatureType, SignedOrder } from '@0xproject/types'; -import { BigNumber, errorUtils } from '@0xproject/utils'; +import { BigNumber, errorUtils, logUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; import { BlockParamLiteral, LogWithDecodedArgs, Provider, TxData } from 'ethereum-types'; @@ -253,7 +253,11 @@ export class CoreCombinatorialUtils { this.exchangeWrapper = exchangeWrapper; this.assetWrapper = assetWrapper; } - public async testFillOrderScenarioAsync(provider: Provider, fillScenario: FillScenario): Promise { + public async testFillOrderScenarioAsync( + provider: Provider, + fillScenario: FillScenario, + isVerbose: boolean = false, + ): Promise { // 1. Generate order const order = this.orderFactory.generateOrder(fillScenario.orderScenario); @@ -304,6 +308,10 @@ export class CoreCombinatorialUtils { this.zrxAssetData, ); } catch (err) { + if (isVerbose) { + logUtils.log(`Expecting order to ${isFillFailureExpected ? 'fail' : 'succeed'} with:`); + logUtils.log(err); + } isFillFailureExpected = true; } -- cgit From 632da71a8d7ab6f4ac64ace407543a8ff9f4d548 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 20 Jun 2018 13:31:21 +0200 Subject: Add TS 2.7 numeric separators to improve readability --- packages/contracts/src/utils/order_factory_from_scenario.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/contracts/src/utils/order_factory_from_scenario.ts b/packages/contracts/src/utils/order_factory_from_scenario.ts index 74c2d4fa2..8a5ded203 100644 --- a/packages/contracts/src/utils/order_factory_from_scenario.ts +++ b/packages/contracts/src/utils/order_factory_from_scenario.ts @@ -15,12 +15,12 @@ import { TakerScenario, } from './types'; -const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10000000000000000000); -const FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(5000000000000000000); -const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100000000000000000); -const POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(50000000000000000); -const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1000000); -const FIVE_UNITS_FIVE_DECIMALS = new BigNumber(500000); +const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10_000_000_000_000_000_000); +const FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(5_000_000_000_000_000_000); +const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100_000_000_000_000_000); +const POINT_ZERO_FIVE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(50_000_000_000_000_000); +const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1_000_000); +const FIVE_UNITS_FIVE_DECIMALS = new BigNumber(500_000); const ONE_NFT_UNIT = new BigNumber(1); export class OrderFactoryFromScenario { -- cgit From 32dea43d2e207ae235f3c7c869d307906a49660a Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 20 Jun 2018 13:31:57 +0200 Subject: Remove unneeded explicit type def --- packages/contracts/src/utils/order_factory_from_scenario.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/src/utils/order_factory_from_scenario.ts b/packages/contracts/src/utils/order_factory_from_scenario.ts index 8a5ded203..b150e59f6 100644 --- a/packages/contracts/src/utils/order_factory_from_scenario.ts +++ b/packages/contracts/src/utils/order_factory_from_scenario.ts @@ -256,7 +256,7 @@ export class OrderFactoryFromScenario { throw errorUtils.spawnSwitchErr('TakerScenario', orderScenario.takerScenario); } - const order: Order = { + const order = { senderAddress: constants.NULL_ADDRESS, makerAddress, takerAddress, -- cgit From 9bc481ff62cf9e4a60794000b8cab840405d63fa Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 20 Jun 2018 13:36:57 +0200 Subject: Split balance and allowance amount types in prep of adding the "unlimited" allowance enum value --- .../src/utils/core_combinatorial_utils.ts | 67 +++++++++++----------- packages/contracts/src/utils/types.ts | 16 ++++-- packages/contracts/test/exchange/fill_order.ts | 27 ++++----- 3 files changed, 59 insertions(+), 51 deletions(-) diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index cf87ad36f..24ef333de 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -29,7 +29,9 @@ import { signingUtils } from '../utils/signing_utils'; import { SimpleAssetBalanceAndProxyAllowanceFetcher } from '../utils/simple_asset_balance_and_proxy_allowance_fetcher'; import { SimpleOrderFilledCancelledFetcher } from '../utils/simple_order_filled_cancelled_fetcher'; import { + AllowanceAmountScenario, AssetDataScenario, + BalanceAmountScenario, ExpirationTimeSecondsScenario, FeeRecipientAddressScenario, FillScenario, @@ -37,7 +39,6 @@ import { OrderScenario, TakerAssetFillAmountScenario, TakerScenario, - TokenAmountScenario, TraderStateScenario, } from '../utils/types'; @@ -192,16 +193,16 @@ export class CoreCombinatorialUtils { }, takerAssetFillAmountScenario: fillScenarioArray[9] as TakerAssetFillAmountScenario, makerStateScenario: { - traderAssetBalance: TokenAmountScenario.Higher, - traderAssetAllowance: TokenAmountScenario.Higher, - zrxFeeBalance: TokenAmountScenario.Higher, - zrxFeeAllowance: TokenAmountScenario.Higher, + traderAssetBalance: BalanceAmountScenario.Higher, + traderAssetAllowance: AllowanceAmountScenario.Higher, + zrxFeeBalance: BalanceAmountScenario.Higher, + zrxFeeAllowance: AllowanceAmountScenario.Higher, }, takerStateScenario: { - traderAssetBalance: TokenAmountScenario.Higher, - traderAssetAllowance: TokenAmountScenario.Higher, - zrxFeeBalance: TokenAmountScenario.Higher, - zrxFeeAllowance: TokenAmountScenario.Higher, + traderAssetBalance: BalanceAmountScenario.Higher, + traderAssetAllowance: AllowanceAmountScenario.Higher, + zrxFeeBalance: BalanceAmountScenario.Higher, + zrxFeeAllowance: AllowanceAmountScenario.Higher, }, }; return fillScenario; @@ -547,10 +548,10 @@ export class CoreCombinatorialUtils { signedOrder.makerAssetAmount, ); switch (makerStateScenario.traderAssetBalance) { - case TokenAmountScenario.Higher: + case BalanceAmountScenario.Higher: break; // Noop since this is already the default - case TokenAmountScenario.TooLow: + case BalanceAmountScenario.TooLow: if (makerAssetFillAmount.eq(0)) { throw new Error(`Cannot set makerAssetBalanceOfMaker TooLow if makerAssetFillAmount is 0`); } @@ -562,7 +563,7 @@ export class CoreCombinatorialUtils { ); break; - case TokenAmountScenario.Exact: + case BalanceAmountScenario.Exact: const exactBalance = makerAssetFillAmount; await this.assetWrapper.setBalanceAsync( signedOrder.makerAddress, @@ -584,10 +585,10 @@ export class CoreCombinatorialUtils { signedOrder.makerFee, ); switch (makerStateScenario.zrxFeeBalance) { - case TokenAmountScenario.Higher: + case BalanceAmountScenario.Higher: break; // Noop since this is already the default - case TokenAmountScenario.TooLow: + case BalanceAmountScenario.TooLow: if (makerFee.eq(0)) { throw new Error(`Cannot set zrxAsserBalanceOfMaker TooLow if makerFee is 0`); } @@ -595,7 +596,7 @@ export class CoreCombinatorialUtils { await this.assetWrapper.setBalanceAsync(signedOrder.makerAddress, this.zrxAssetData, tooLowBalance); break; - case TokenAmountScenario.Exact: + case BalanceAmountScenario.Exact: const exactBalance = makerFee; await this.assetWrapper.setBalanceAsync(signedOrder.makerAddress, this.zrxAssetData, exactBalance); break; @@ -605,10 +606,10 @@ export class CoreCombinatorialUtils { } switch (makerStateScenario.traderAssetAllowance) { - case TokenAmountScenario.Higher: + case AllowanceAmountScenario.Higher: break; // Noop since this is already the default - case TokenAmountScenario.TooLow: + case AllowanceAmountScenario.TooLow: const tooLowAllowance = makerAssetFillAmount.minus(1); await this.assetWrapper.setProxyAllowanceAsync( signedOrder.makerAddress, @@ -617,7 +618,7 @@ export class CoreCombinatorialUtils { ); break; - case TokenAmountScenario.Exact: + case AllowanceAmountScenario.Exact: const exactAllowance = makerAssetFillAmount; await this.assetWrapper.setProxyAllowanceAsync( signedOrder.makerAddress, @@ -634,10 +635,10 @@ export class CoreCombinatorialUtils { } switch (makerStateScenario.zrxFeeAllowance) { - case TokenAmountScenario.Higher: + case AllowanceAmountScenario.Higher: break; // Noop since this is already the default - case TokenAmountScenario.TooLow: + case AllowanceAmountScenario.TooLow: const tooLowAllowance = makerFee.minus(1); await this.assetWrapper.setProxyAllowanceAsync( signedOrder.makerAddress, @@ -646,7 +647,7 @@ export class CoreCombinatorialUtils { ); break; - case TokenAmountScenario.Exact: + case AllowanceAmountScenario.Exact: const exactAllowance = makerFee; await this.assetWrapper.setProxyAllowanceAsync( signedOrder.makerAddress, @@ -663,10 +664,10 @@ export class CoreCombinatorialUtils { } switch (takerStateScenario.traderAssetBalance) { - case TokenAmountScenario.Higher: + case BalanceAmountScenario.Higher: break; // Noop since this is already the default - case TokenAmountScenario.TooLow: + case BalanceAmountScenario.TooLow: if (takerAssetFillAmount.eq(0)) { throw new Error(`Cannot set takerAssetBalanceOfTaker TooLow if takerAssetFillAmount is 0`); } @@ -674,7 +675,7 @@ export class CoreCombinatorialUtils { await this.assetWrapper.setBalanceAsync(this.takerAddress, signedOrder.takerAssetData, tooLowBalance); break; - case TokenAmountScenario.Exact: + case BalanceAmountScenario.Exact: const exactBalance = takerAssetFillAmount; await this.assetWrapper.setBalanceAsync(this.takerAddress, signedOrder.takerAssetData, exactBalance); break; @@ -692,10 +693,10 @@ export class CoreCombinatorialUtils { signedOrder.takerFee, ); switch (takerStateScenario.zrxFeeBalance) { - case TokenAmountScenario.Higher: + case BalanceAmountScenario.Higher: break; // Noop since this is already the default - case TokenAmountScenario.TooLow: + case BalanceAmountScenario.TooLow: if (takerFee.eq(0)) { throw new Error(`Cannot set zrxAssetBalanceOfTaker TooLow if takerFee is 0`); } @@ -703,7 +704,7 @@ export class CoreCombinatorialUtils { await this.assetWrapper.setBalanceAsync(this.takerAddress, this.zrxAssetData, tooLowBalance); break; - case TokenAmountScenario.Exact: + case BalanceAmountScenario.Exact: const exactBalance = takerFee; await this.assetWrapper.setBalanceAsync(this.takerAddress, this.zrxAssetData, exactBalance); break; @@ -713,10 +714,10 @@ export class CoreCombinatorialUtils { } switch (takerStateScenario.traderAssetAllowance) { - case TokenAmountScenario.Higher: + case AllowanceAmountScenario.Higher: break; // Noop since this is already the default - case TokenAmountScenario.TooLow: + case AllowanceAmountScenario.TooLow: const tooLowAllowance = takerAssetFillAmount.minus(1); await this.assetWrapper.setProxyAllowanceAsync( this.takerAddress, @@ -725,7 +726,7 @@ export class CoreCombinatorialUtils { ); break; - case TokenAmountScenario.Exact: + case AllowanceAmountScenario.Exact: const exactAllowance = takerAssetFillAmount; await this.assetWrapper.setProxyAllowanceAsync( this.takerAddress, @@ -742,10 +743,10 @@ export class CoreCombinatorialUtils { } switch (takerStateScenario.zrxFeeAllowance) { - case TokenAmountScenario.Higher: + case AllowanceAmountScenario.Higher: break; // Noop since this is already the default - case TokenAmountScenario.TooLow: + case AllowanceAmountScenario.TooLow: const tooLowAllowance = takerFee.minus(1); await this.assetWrapper.setProxyAllowanceAsync( signedOrder.takerAddress, @@ -754,7 +755,7 @@ export class CoreCombinatorialUtils { ); break; - case TokenAmountScenario.Exact: + case AllowanceAmountScenario.Exact: const exactAllowance = takerFee; await this.assetWrapper.setProxyAllowanceAsync( signedOrder.takerAddress, diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 5be65fb1d..0b419d672 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -201,17 +201,23 @@ export interface OrderScenario { takerAssetDataScenario: AssetDataScenario; } -export enum TokenAmountScenario { +export enum BalanceAmountScenario { + Exact = 'EXACT', + TooLow = 'TOO_LOW', + Higher = 'HIGHER', +} + +export enum AllowanceAmountScenario { Exact = 'EXACT', TooLow = 'TOO_LOW', Higher = 'HIGHER', } export interface TraderStateScenario { - traderAssetBalance: TokenAmountScenario; - traderAssetAllowance: TokenAmountScenario; - zrxFeeBalance: TokenAmountScenario; - zrxFeeAllowance: TokenAmountScenario; + traderAssetBalance: BalanceAmountScenario; + traderAssetAllowance: AllowanceAmountScenario; + zrxFeeBalance: BalanceAmountScenario; + zrxFeeAllowance: AllowanceAmountScenario; } export interface FillScenario { diff --git a/packages/contracts/test/exchange/fill_order.ts b/packages/contracts/test/exchange/fill_order.ts index 5e8dd6ffc..dc1dee91f 100644 --- a/packages/contracts/test/exchange/fill_order.ts +++ b/packages/contracts/test/exchange/fill_order.ts @@ -6,14 +6,15 @@ import { CoreCombinatorialUtils, coreCombinatorialUtilsFactoryAsync } from '../. import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; import { + AllowanceAmountScenario, AssetDataScenario, + BalanceAmountScenario, ExpirationTimeSecondsScenario, FeeRecipientAddressScenario, OrderAssetAmountScenario, OrderScenario, TakerAssetFillAmountScenario, TakerScenario, - TokenAmountScenario, } from '../../src/utils/types'; chaiSetup.configure(); @@ -33,16 +34,16 @@ const defaultFillScenario = { }, takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount, makerStateScenario: { - traderAssetBalance: TokenAmountScenario.Higher, - traderAssetAllowance: TokenAmountScenario.Higher, - zrxFeeBalance: TokenAmountScenario.Higher, - zrxFeeAllowance: TokenAmountScenario.Higher, + traderAssetBalance: BalanceAmountScenario.Higher, + traderAssetAllowance: AllowanceAmountScenario.Higher, + zrxFeeBalance: BalanceAmountScenario.Higher, + zrxFeeAllowance: AllowanceAmountScenario.Higher, }, takerStateScenario: { - traderAssetBalance: TokenAmountScenario.Higher, - traderAssetAllowance: TokenAmountScenario.Higher, - zrxFeeBalance: TokenAmountScenario.Higher, - zrxFeeAllowance: TokenAmountScenario.Higher, + traderAssetBalance: BalanceAmountScenario.Higher, + traderAssetAllowance: AllowanceAmountScenario.Higher, + zrxFeeBalance: BalanceAmountScenario.Higher, + zrxFeeAllowance: AllowanceAmountScenario.Higher, }, }; @@ -163,7 +164,7 @@ describe('FillOrder Tests', () => { ...defaultFillScenario, makerStateScenario: { ...defaultFillScenario.makerStateScenario, - traderAssetBalance: TokenAmountScenario.TooLow, + traderAssetBalance: BalanceAmountScenario.TooLow, }, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); @@ -174,7 +175,7 @@ describe('FillOrder Tests', () => { ...defaultFillScenario, takerStateScenario: { ...defaultFillScenario.makerStateScenario, - traderAssetBalance: TokenAmountScenario.TooLow, + traderAssetBalance: BalanceAmountScenario.TooLow, }, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); @@ -185,7 +186,7 @@ describe('FillOrder Tests', () => { ...defaultFillScenario, makerStateScenario: { ...defaultFillScenario.makerStateScenario, - traderAssetAllowance: TokenAmountScenario.TooLow, + traderAssetAllowance: AllowanceAmountScenario.TooLow, }, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); @@ -196,7 +197,7 @@ describe('FillOrder Tests', () => { ...defaultFillScenario, takerStateScenario: { ...defaultFillScenario.makerStateScenario, - traderAssetAllowance: TokenAmountScenario.TooLow, + traderAssetAllowance: AllowanceAmountScenario.TooLow, }, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); -- cgit From 0bbdbc9e63fbd73158c1ffd76eb3f3e4d0660598 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 20 Jun 2018 13:55:28 +0200 Subject: Rename for clarity --- packages/contracts/src/utils/asset_wrapper.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/contracts/src/utils/asset_wrapper.ts b/packages/contracts/src/utils/asset_wrapper.ts index 2eb54f290..f86c0bc23 100644 --- a/packages/contracts/src/utils/asset_wrapper.ts +++ b/packages/contracts/src/utils/asset_wrapper.ts @@ -29,8 +29,8 @@ export class AssetWrapper { const proxyId = assetProxyUtils.decodeAssetDataId(assetData); switch (proxyId) { case constants.ERC20_PROXY_ID: { - const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; - const balance = await assetWrapper.getBalanceAsync(userAddress, assetData); + const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; + const balance = await erc20Wrapper.getBalanceAsync(userAddress, assetData); return balance; } case constants.ERC721_PROXY_ID: { @@ -52,8 +52,8 @@ export class AssetWrapper { const proxyId = assetProxyUtils.decodeAssetDataId(assetData); switch (proxyId) { case constants.ERC20_PROXY_ID: { - const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; - await assetWrapper.setBalanceAsync(userAddress, assetData, desiredBalance); + const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; + await erc20Wrapper.setBalanceAsync(userAddress, assetData, desiredBalance); return; } case constants.ERC721_PROXY_ID: { @@ -98,8 +98,8 @@ export class AssetWrapper { const proxyId = assetProxyUtils.decodeAssetDataId(assetData); switch (proxyId) { case constants.ERC20_PROXY_ID: { - const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; - const allowance = await assetWrapper.getProxyAllowanceAsync(userAddress, assetData); + const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; + const allowance = await erc20Wrapper.getProxyAllowanceAsync(userAddress, assetData); return allowance; } case constants.ERC721_PROXY_ID: { @@ -129,8 +129,8 @@ export class AssetWrapper { const proxyId = assetProxyUtils.decodeAssetDataId(assetData); switch (proxyId) { case constants.ERC20_PROXY_ID: { - const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; - await assetWrapper.setAllowanceAsync(userAddress, assetData, desiredAllowance); + const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; + await erc20Wrapper.setAllowanceAsync(userAddress, assetData, desiredAllowance); return; } case constants.ERC721_PROXY_ID: { -- cgit From 3fab40efe553446770c8858afd0020f613ca4d4c Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 20 Jun 2018 13:55:45 +0200 Subject: Add noop conditionals for clarity --- packages/contracts/src/utils/asset_wrapper.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/contracts/src/utils/asset_wrapper.ts b/packages/contracts/src/utils/asset_wrapper.ts index f86c0bc23..11697eca8 100644 --- a/packages/contracts/src/utils/asset_wrapper.ts +++ b/packages/contracts/src/utils/asset_wrapper.ts @@ -87,6 +87,11 @@ export class AssetWrapper { // Burn token await erc721Wrapper.burnAsync(assetProxyData.tokenAddress, assetProxyData.tokenId, userAddress); return; + } else if ( + (userAddress !== tokenOwner && desiredBalance.eq(0)) || + (tokenOwner === userAddress && desiredBalance.eq(1)) + ) { + return; // noop } break; } @@ -176,6 +181,11 @@ export class AssetWrapper { assetProxyData.tokenAddress, assetProxyData.tokenId, ); + } else if ( + (!isProxyApproved && desiredAllowance.eq(0)) || + (isProxyApproved && desiredAllowance.eq(1)) + ) { + return; // noop } break; -- cgit From fb7d425244fd754c478f432c5a64d52917071fbc Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 20 Jun 2018 13:56:27 +0200 Subject: Add support for setting allowance to unlimited --- packages/contracts/src/utils/asset_wrapper.ts | 31 ++++++++++++++++++++------ packages/contracts/src/utils/erc721_wrapper.ts | 11 +++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/packages/contracts/src/utils/asset_wrapper.ts b/packages/contracts/src/utils/asset_wrapper.ts index 11697eca8..fd543621f 100644 --- a/packages/contracts/src/utils/asset_wrapper.ts +++ b/packages/contracts/src/utils/asset_wrapper.ts @@ -139,8 +139,14 @@ export class AssetWrapper { return; } case constants.ERC721_PROXY_ID: { - if (!desiredAllowance.eq(0) && !desiredAllowance.eq(1)) { - throw new Error(`Allowance for ERC721 token can only be set to 0 or 1. Got: ${desiredAllowance}`); + if ( + !desiredAllowance.eq(0) && + !desiredAllowance.eq(1) && + !desiredAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS) + ) { + throw new Error( + `Allowance for ERC721 token can only be set to 0, 1 or 2^256-1. Got: ${desiredAllowance}`, + ); } const erc721Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper; const assetProxyData = assetProxyUtils.decodeERC721AssetData(assetData); @@ -161,11 +167,22 @@ export class AssetWrapper { assetProxyData.tokenAddress, assetProxyData.tokenId, ); - // HACK: We do not currently support ApprovedForAll when setting proxy allowance - // This was intentional since unsetting ApprovedForAll, will unset approval for unrelated - // tokens other then the one specified in the call to this method. - if (isProxyApprovedForAll) { - throw new Error(`We don't currently support the use of "approveAll" functionality for ERC721.`); + if (!isProxyApprovedForAll && desiredAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) { + const isApproved = true; + await erc721Wrapper.approveProxyForAllAsync( + assetProxyData.tokenAddress, + assetProxyData.tokenId, + isApproved, + ); + } else if (isProxyApprovedForAll && desiredAllowance.eq(0)) { + const isApproved = false; + await erc721Wrapper.approveProxyForAllAsync( + assetProxyData.tokenAddress, + assetProxyData.tokenId, + isApproved, + ); + } else if (isProxyApprovedForAll && desiredAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) { + return; // Noop } const isProxyApproved = await erc721Wrapper.isProxyApprovedAsync( diff --git a/packages/contracts/src/utils/erc721_wrapper.ts b/packages/contracts/src/utils/erc721_wrapper.ts index 7da3b6781..6df9fa12e 100644 --- a/packages/contracts/src/utils/erc721_wrapper.ts +++ b/packages/contracts/src/utils/erc721_wrapper.ts @@ -88,6 +88,17 @@ export class ERC721Wrapper { const proxyAddress = (this._proxyContract as ERC721ProxyContract).address; await this.approveAsync(proxyAddress, tokenAddress, tokenId); } + public async approveProxyForAllAsync(tokenAddress: string, tokenId: BigNumber, isApproved: boolean): Promise { + const tokenContract = this._getTokenContractFromAssetData(tokenAddress); + const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId); + const proxyAddress = (this._proxyContract as ERC721ProxyContract).address; + await this._web3Wrapper.awaitTransactionSuccessAsync( + await tokenContract.setApprovalForAll.sendTransactionAsync(proxyAddress, isApproved, { + from: tokenOwner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + } public async approveAsync(to: string, tokenAddress: string, tokenId: BigNumber): Promise { const tokenContract = this._getTokenContractFromAssetData(tokenAddress); const tokenOwner = await this.ownerOfAsync(tokenAddress, tokenId); -- cgit From 5c0183c71ef9f988c6a7c799c177df53c222acb3 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 20 Jun 2018 14:08:56 +0200 Subject: Fix merge variable name issue --- packages/contracts/src/utils/core_combinatorial_utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index 24ef333de..4d6f3b965 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -263,7 +263,7 @@ export class CoreCombinatorialUtils { const order = this.orderFactory.generateOrder(fillScenario.orderScenario); // 2. Sign order - const orderHashBuff = orderHashUtils.getOrderHashBuff(order); + const orderHashBuff = orderHashUtils.getOrderHashBuffer(order); const signature = signingUtils.signMessage(orderHashBuff, this.makerPrivateKey, SignatureType.EthSign); const signedOrder = { ...order, -- cgit From 3ce449e16739896f81b171d130afbb5774829d64 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 20 Jun 2018 19:12:31 +0200 Subject: Improve verbose logging --- packages/contracts/src/utils/core_combinatorial_utils.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index 4d6f3b965..f1a5599ef 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -308,12 +308,15 @@ export class CoreCombinatorialUtils { this.takerAddress, this.zrxAssetData, ); + if (isVerbose) { + logUtils.log(`Expecting fillOrder to succeed.`); + } } catch (err) { + isFillFailureExpected = true; if (isVerbose) { - logUtils.log(`Expecting order to ${isFillFailureExpected ? 'fail' : 'succeed'} with:`); + logUtils.log(`Expecting fillOrder to fail with:`); logUtils.log(err); } - isFillFailureExpected = true; } // 6. Fill the order -- cgit From 247f8c8557a501ea5081615d9b031c9e4b7aeec7 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 20 Jun 2018 19:13:30 +0200 Subject: Fix orderValidationUtils where was confusing a makerAssetAmount as a takerAssetAmount --- packages/order-utils/src/order_state_utils.ts | 9 ++++++++- packages/order-utils/src/order_validation_utils.ts | 13 +++---------- packages/order-utils/src/utils.ts | 7 +++++++ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts index dbb65de59..3752fdadb 100644 --- a/packages/order-utils/src/order_state_utils.ts +++ b/packages/order-utils/src/order_state_utils.ts @@ -14,6 +14,7 @@ import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_f import { constants } from './constants'; import { orderHashUtils } from './order_hash'; import { RemainingFillableCalculator } from './remaining_fillable_calculator'; +import { utils } from './utils'; interface SidedOrderRelevantState { isMakerSide: boolean; @@ -161,7 +162,13 @@ export class OrderStateUtils { signedOrder, signedOrder.takerAddress, ); - const remainingFillableTakerAssetAmountGivenMakersStatus = orderRelevantMakerState.remainingFillableAssetAmount; + const remainingFillableTakerAssetAmountGivenMakersStatus = signedOrder.makerAssetAmount.eq(0) + ? new BigNumber(0) + : utils.getPartialAmount( + orderRelevantMakerState.remainingFillableAssetAmount, + signedOrder.makerAssetAmount, + signedOrder.takerAssetAmount, + ); // Get max fillable amount for an order, considering the takers ability to fill isMaker = false; diff --git a/packages/order-utils/src/order_validation_utils.ts b/packages/order-utils/src/order_validation_utils.ts index fb96502f2..da5b37456 100644 --- a/packages/order-utils/src/order_validation_utils.ts +++ b/packages/order-utils/src/order_validation_utils.ts @@ -54,7 +54,7 @@ export class OrderValidationUtils { senderAddress: string, zrxAssetData: string, ): Promise { - const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount( + const fillMakerTokenAmount = utils.getPartialAmount( fillTakerAssetAmount, signedOrder.takerAssetAmount, signedOrder.makerAssetAmount, @@ -75,7 +75,7 @@ export class OrderValidationUtils { TradeSide.Taker, TransferType.Trade, ); - const makerFeeAmount = OrderValidationUtils._getPartialAmount( + const makerFeeAmount = utils.getPartialAmount( fillTakerAssetAmount, signedOrder.takerAssetAmount, signedOrder.makerFee, @@ -88,7 +88,7 @@ export class OrderValidationUtils { TradeSide.Maker, TransferType.Fee, ); - const takerFeeAmount = OrderValidationUtils._getPartialAmount( + const takerFeeAmount = utils.getPartialAmount( fillTakerAssetAmount, signedOrder.takerAssetAmount, signedOrder.takerFee, @@ -116,13 +116,6 @@ export class OrderValidationUtils { throw new Error(ExchangeContractErrs.OrderFillExpired); } } - private static _getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber { - const fillMakerTokenAmount = numerator - .mul(target) - .div(denominator) - .round(0); - return fillMakerTokenAmount; - } constructor(orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher) { this._orderFilledCancelledFetcher = orderFilledCancelledFetcher; } diff --git a/packages/order-utils/src/utils.ts b/packages/order-utils/src/utils.ts index 6149316f6..7aaaf0609 100644 --- a/packages/order-utils/src/utils.ts +++ b/packages/order-utils/src/utils.ts @@ -12,4 +12,11 @@ export const utils = { const milisecondsInSecond = 1000; return new BigNumber(Date.now() / milisecondsInSecond).round(); }, + getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber { + const fillMakerTokenAmount = numerator + .mul(target) + .div(denominator) + .round(0); + return fillMakerTokenAmount; + }, }; -- cgit From 5bfdffda1155e306b17eee0a4e60320c3433d5c4 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 20 Jun 2018 19:14:04 +0200 Subject: Add support for approveAll in assetWrapper and fillOrderScenarios --- packages/contracts/src/utils/asset_wrapper.ts | 12 ++++--- .../src/utils/core_combinatorial_utils.ts | 32 +++++++++++++++++ packages/contracts/src/utils/types.ts | 1 + packages/contracts/test/exchange/fill_order.ts | 40 +++++++++++++++++++++- 4 files changed, 80 insertions(+), 5 deletions(-) diff --git a/packages/contracts/src/utils/asset_wrapper.ts b/packages/contracts/src/utils/asset_wrapper.ts index fd543621f..9c683054d 100644 --- a/packages/contracts/src/utils/asset_wrapper.ts +++ b/packages/contracts/src/utils/asset_wrapper.ts @@ -110,16 +110,20 @@ export class AssetWrapper { case constants.ERC721_PROXY_ID: { const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper; const erc721ProxyData = assetProxyUtils.decodeERC721AssetData(assetData); - const isProxyApproved = await assetWrapper.isProxyApprovedAsync( + const isProxyApprovedForAll = await assetWrapper.isProxyApprovedForAllAsync( + userAddress, erc721ProxyData.tokenAddress, erc721ProxyData.tokenId, ); - const isProxyApprovedForAllAsync = await assetWrapper.isProxyApprovedForAllAsync( - userAddress, + if (isProxyApprovedForAll) { + return constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; + } + + const isProxyApproved = await assetWrapper.isProxyApprovedAsync( erc721ProxyData.tokenAddress, erc721ProxyData.tokenId, ); - const allowance = isProxyApproved || isProxyApprovedForAllAsync ? new BigNumber(1) : new BigNumber(0); + const allowance = isProxyApproved ? new BigNumber(1) : new BigNumber(0); return allowance; } default: diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index f1a5599ef..7e9d18e46 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -630,6 +630,14 @@ export class CoreCombinatorialUtils { ); break; + case AllowanceAmountScenario.Unlimited: + await this.assetWrapper.setProxyAllowanceAsync( + signedOrder.makerAddress, + signedOrder.makerAssetData, + constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, + ); + break; + default: throw errorUtils.spawnSwitchErr( 'makerStateScenario.traderAssetAllowance', @@ -659,6 +667,14 @@ export class CoreCombinatorialUtils { ); break; + case AllowanceAmountScenario.Unlimited: + await this.assetWrapper.setProxyAllowanceAsync( + signedOrder.makerAddress, + this.zrxAssetData, + constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, + ); + break; + default: throw errorUtils.spawnSwitchErr( 'makerStateScenario.zrxFeeAllowance', @@ -738,6 +754,14 @@ export class CoreCombinatorialUtils { ); break; + case AllowanceAmountScenario.Unlimited: + await this.assetWrapper.setProxyAllowanceAsync( + this.takerAddress, + signedOrder.takerAssetData, + constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, + ); + break; + default: throw errorUtils.spawnSwitchErr( 'takerStateScenario.traderAssetAllowance', @@ -767,6 +791,14 @@ export class CoreCombinatorialUtils { ); break; + case AllowanceAmountScenario.Unlimited: + await this.assetWrapper.setProxyAllowanceAsync( + signedOrder.takerAddress, + this.zrxAssetData, + constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, + ); + break; + default: throw errorUtils.spawnSwitchErr( 'takerStateScenario.zrxFeeAllowance', diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 0b419d672..7d9217c50 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -211,6 +211,7 @@ export enum AllowanceAmountScenario { Exact = 'EXACT', TooLow = 'TOO_LOW', Higher = 'HIGHER', + Unlimited = 'UNLIMITED', } export interface TraderStateScenario { diff --git a/packages/contracts/test/exchange/fill_order.ts b/packages/contracts/test/exchange/fill_order.ts index dc1dee91f..56d7510f4 100644 --- a/packages/contracts/test/exchange/fill_order.ts +++ b/packages/contracts/test/exchange/fill_order.ts @@ -228,7 +228,7 @@ describe('FillOrder Tests', () => { }, takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount, }; - await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario, true); }); it('should successfully fill order when makerAsset is ERC20 and takerAsset is ERC721', async () => { @@ -243,5 +243,43 @@ describe('FillOrder Tests', () => { }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); + + it('should successfully fill order when makerAsset is ERC721 and approveAll is set for it', async () => { + const fillScenario = { + ...defaultFillScenario, + orderScenario: { + ...defaultFillScenario.orderScenario, + makerAssetDataScenario: AssetDataScenario.ERC721, + takerAssetDataScenario: AssetDataScenario.ERC20NonZRXEighteenDecimals, + }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount, + makerStateScenario: { + ...defaultFillScenario.makerStateScenario, + traderAssetAllowance: AllowanceAmountScenario.Unlimited, + }, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); + + it('should successfully fill order when makerAsset and takerAsset are ERC721 and approveAll is set for them', async () => { + const fillScenario = { + ...defaultFillScenario, + orderScenario: { + ...defaultFillScenario.orderScenario, + makerAssetDataScenario: AssetDataScenario.ERC721, + takerAssetDataScenario: AssetDataScenario.ERC721, + }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount, + makerStateScenario: { + ...defaultFillScenario.makerStateScenario, + traderAssetAllowance: AllowanceAmountScenario.Unlimited, + }, + takerStateScenario: { + ...defaultFillScenario.takerStateScenario, + traderAssetAllowance: AllowanceAmountScenario.Unlimited, + }, + }; + await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); + }); }); }); -- cgit From bbd414bdcd3bcbe3a00ff38664cd9c6e488a7d6a Mon Sep 17 00:00:00 2001 From: perissology Date: Fri, 22 Jun 2018 15:47:44 -0700 Subject: add ability to ignore covering specific code blocks --- packages/sol-cov/src/ast_visitor.ts | 29 +++++++++++++++++++++++- packages/sol-cov/src/collect_coverage_entries.ts | 21 ++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/packages/sol-cov/src/ast_visitor.ts b/packages/sol-cov/src/ast_visitor.ts index 564f0f7d2..49b7ac4bd 100644 --- a/packages/sol-cov/src/ast_visitor.ts +++ b/packages/sol-cov/src/ast_visitor.ts @@ -23,8 +23,11 @@ export class ASTVisitor { private _modifiersStatementIds: number[] = []; private _statementMap: StatementMap = {}; private _locationByOffset: LocationByOffset; - constructor(locationByOffset: LocationByOffset) { + private _ignoreRangesBeginingAt: number[]; + private _ignoreRangesWithin: Array<[number, number]> = []; + constructor(locationByOffset: LocationByOffset, ignoreRangesBeginingAt: number[] = []) { this._locationByOffset = locationByOffset; + this._ignoreRangesBeginingAt = ignoreRangesBeginingAt; } public getCollectedCoverageEntries(): CoverageEntriesDescription { const coverageEntriesDescription = { @@ -96,6 +99,9 @@ export class ASTVisitor { public ModifierInvocation(ast: Parser.ModifierInvocation): void { const BUILTIN_MODIFIERS = ['public', 'view', 'payable', 'external', 'internal', 'pure', 'constant']; if (!_.includes(BUILTIN_MODIFIERS, ast.name)) { + if (this._ignoreExpression(ast)) { + return; + } this._modifiersStatementIds.push(this._entryId); this._visitStatement(ast); } @@ -106,6 +112,9 @@ export class ASTVisitor { right: Parser.ASTNode, type: BranchType, ): void { + if (this._ignoreExpression(ast)) { + return; + } this._branchMap[this._entryId++] = { line: this._getExpressionRange(ast).start.line, type, @@ -113,6 +122,9 @@ export class ASTVisitor { }; } private _visitStatement(ast: Parser.ASTNode): void { + if (this._ignoreExpression(ast)) { + return; + } this._statementMap[this._entryId++] = this._getExpressionRange(ast); } private _getExpressionRange(ast: Parser.ASTNode): SingleFileSourceRange { @@ -125,7 +137,22 @@ export class ASTVisitor { }; return range; } + private _ignoreExpression(ast: Parser.ASTNode): boolean { + const [astStart, astEnd] = ast.range as [number, number]; + const isRangeIgnored = _.some( + this._ignoreRangesWithin, + ([rangeStart, rangeEnd]: [number, number]) => astStart >= rangeStart && astEnd <= rangeEnd, + ); + return this._ignoreRangesBeginingAt.includes(astStart) || isRangeIgnored; + } private _visitFunctionLikeDefinition(ast: Parser.ModifierDefinition | Parser.FunctionDefinition): void { + if (this._ignoreExpression(ast)) { + // we want to ignore everything within this function + // add this nodes range to the ignoreRangesWithin array + // so we can ignore any later nodes that are children of this node + this._ignoreRangesWithin.push(ast.range as [number, number]); + return; + } const loc = this._getExpressionRange(ast); this._fnMap[this._entryId++] = { name: ast.name, diff --git a/packages/sol-cov/src/collect_coverage_entries.ts b/packages/sol-cov/src/collect_coverage_entries.ts index 3fc85008c..703af3099 100644 --- a/packages/sol-cov/src/collect_coverage_entries.ts +++ b/packages/sol-cov/src/collect_coverage_entries.ts @@ -13,10 +13,29 @@ export const collectCoverageEntries = (contractSource: string) => { if (_.isUndefined(coverageEntriesBySourceHash[sourceHash]) && !_.isUndefined(contractSource)) { const ast = parser.parse(contractSource, { range: true }); const locationByOffset = getLocationByOffset(contractSource); - const visitor = new ASTVisitor(locationByOffset); + const ignoreRangesBegingingAt = gatherRangesToIgnore(contractSource); + const visitor = new ASTVisitor(locationByOffset, ignoreRangesBegingingAt); parser.visit(ast, visitor); coverageEntriesBySourceHash[sourceHash] = visitor.getCollectedCoverageEntries(); } const coverageEntriesDescription = coverageEntriesBySourceHash[sourceHash]; return coverageEntriesDescription; }; + +const IGNORE_RE = /\/\*\s*solcov\s+ignore\s+next\s*\*\/\s*/gm; + +// Gather the start index of all code blocks preceeded by "/* solcov ignore next */" +function gatherRangesToIgnore(contractSource: string): number[] { + const ignoreRangesStart = []; + + let match; + do { + match = IGNORE_RE.exec(contractSource); + if (match) { + const matchLen = match[0].length; + ignoreRangesStart.push(match.index + matchLen); + } + } while (match); + + return ignoreRangesStart; +} -- cgit From 3ed4a1ba206b52b76488cb85a7e20a277d5c0a2b Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 25 Jun 2018 12:47:13 +0200 Subject: Add missing import --- packages/contracts/test/asset_proxy/authorizable.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/contracts/test/asset_proxy/authorizable.ts b/packages/contracts/test/asset_proxy/authorizable.ts index 24ec73a64..dfee83cf2 100644 --- a/packages/contracts/test/asset_proxy/authorizable.ts +++ b/packages/contracts/test/asset_proxy/authorizable.ts @@ -4,7 +4,10 @@ import * as chai from 'chai'; import { MixinAuthorizableContract } from '../../src/generated_contract_wrappers/mixin_authorizable'; import { artifacts } from '../../src/utils/artifacts'; -import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; +import { + expectRevertOrAlwaysFailingTransactionAsync, + expectRevertReasonOrAlwaysFailingTransactionAsync, +} from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { RevertReasons } from '../../src/utils/types'; -- cgit From ad67a6add54cc31f4b888dbbfe70fed75b743745 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 25 Jun 2018 13:51:00 +0200 Subject: Check revert reasons in declarative and combinatorial tests --- .../src/utils/core_combinatorial_utils.ts | 15 +-- packages/order-utils/src/order_validation_utils.ts | 113 +++++++++++---------- 2 files changed, 67 insertions(+), 61 deletions(-) diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index 7e9d18e46..aea9a65e7 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -16,7 +16,7 @@ import 'make-promises-safe'; import { ExchangeContract, FillContractEventArgs } from '../generated_contract_wrappers/exchange'; import { artifacts } from '../utils/artifacts'; -import { expectRevertOrAlwaysFailingTransactionAsync } from '../utils/assertions'; +import { expectRevertReasonOrAlwaysFailingTransactionAsync } from '../utils/assertions'; import { AssetWrapper } from '../utils/asset_wrapper'; import { chaiSetup } from '../utils/chai_setup'; import { constants } from '../utils/constants'; @@ -298,7 +298,7 @@ export class CoreCombinatorialUtils { const lazyStore = new BalanceAndProxyAllowanceLazyStore(balanceAndProxyAllowanceFetcher); const exchangeTransferSimulator = new ExchangeTransferSimulator(lazyStore); - let isFillFailureExpected = false; + let fillRevertReasonIfExists; try { await orderValidationUtils.validateFillOrderThrowIfInvalidAsync( exchangeTransferSimulator, @@ -312,7 +312,7 @@ export class CoreCombinatorialUtils { logUtils.log(`Expecting fillOrder to succeed.`); } } catch (err) { - isFillFailureExpected = true; + fillRevertReasonIfExists = err.message; if (isVerbose) { logUtils.log(`Expecting fillOrder to fail with:`); logUtils.log(err); @@ -324,7 +324,7 @@ export class CoreCombinatorialUtils { signedOrder, takerAssetFillAmount, lazyStore, - isFillFailureExpected, + fillRevertReasonIfExists, provider, ); } @@ -332,12 +332,13 @@ export class CoreCombinatorialUtils { signedOrder: SignedOrder, takerAssetFillAmount: BigNumber, lazyStore: BalanceAndProxyAllowanceLazyStore, - isFillFailureExpected: boolean, + fillRevertReasonIfExists: string | undefined, provider: Provider, ): Promise { - if (isFillFailureExpected) { - return expectRevertOrAlwaysFailingTransactionAsync( + if (!_.isUndefined(fillRevertReasonIfExists)) { + return expectRevertReasonOrAlwaysFailingTransactionAsync( this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { takerAssetFillAmount }), + fillRevertReasonIfExists, ); } diff --git a/packages/order-utils/src/order_validation_utils.ts b/packages/order-utils/src/order_validation_utils.ts index da5b37456..d2449b183 100644 --- a/packages/order-utils/src/order_validation_utils.ts +++ b/packages/order-utils/src/order_validation_utils.ts @@ -1,4 +1,5 @@ -import { ExchangeContractErrs, Order, SignedOrder } from '@0xproject/types'; +import { Order, SignedOrder } from '@0xproject/types'; +import { RevertReasons } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; @@ -54,66 +55,70 @@ export class OrderValidationUtils { senderAddress: string, zrxAssetData: string, ): Promise { - const fillMakerTokenAmount = utils.getPartialAmount( - fillTakerAssetAmount, - signedOrder.takerAssetAmount, - signedOrder.makerAssetAmount, - ); - await exchangeTradeEmulator.transferFromAsync( - signedOrder.makerAssetData, - signedOrder.makerAddress, - senderAddress, - fillMakerTokenAmount, - TradeSide.Maker, - TransferType.Trade, - ); - await exchangeTradeEmulator.transferFromAsync( - signedOrder.takerAssetData, - senderAddress, - signedOrder.makerAddress, - fillTakerAssetAmount, - TradeSide.Taker, - TransferType.Trade, - ); - const makerFeeAmount = utils.getPartialAmount( - fillTakerAssetAmount, - signedOrder.takerAssetAmount, - signedOrder.makerFee, - ); - await exchangeTradeEmulator.transferFromAsync( - zrxAssetData, - signedOrder.makerAddress, - signedOrder.feeRecipientAddress, - makerFeeAmount, - TradeSide.Maker, - TransferType.Fee, - ); - const takerFeeAmount = utils.getPartialAmount( - fillTakerAssetAmount, - signedOrder.takerAssetAmount, - signedOrder.takerFee, - ); - await exchangeTradeEmulator.transferFromAsync( - zrxAssetData, - senderAddress, - signedOrder.feeRecipientAddress, - takerFeeAmount, - TradeSide.Taker, - TransferType.Fee, - ); + try { + const fillMakerTokenAmount = utils.getPartialAmount( + fillTakerAssetAmount, + signedOrder.takerAssetAmount, + signedOrder.makerAssetAmount, + ); + await exchangeTradeEmulator.transferFromAsync( + signedOrder.makerAssetData, + signedOrder.makerAddress, + senderAddress, + fillMakerTokenAmount, + TradeSide.Maker, + TransferType.Trade, + ); + await exchangeTradeEmulator.transferFromAsync( + signedOrder.takerAssetData, + senderAddress, + signedOrder.makerAddress, + fillTakerAssetAmount, + TradeSide.Taker, + TransferType.Trade, + ); + const makerFeeAmount = utils.getPartialAmount( + fillTakerAssetAmount, + signedOrder.takerAssetAmount, + signedOrder.makerFee, + ); + await exchangeTradeEmulator.transferFromAsync( + zrxAssetData, + signedOrder.makerAddress, + signedOrder.feeRecipientAddress, + makerFeeAmount, + TradeSide.Maker, + TransferType.Fee, + ); + const takerFeeAmount = utils.getPartialAmount( + fillTakerAssetAmount, + signedOrder.takerAssetAmount, + signedOrder.takerFee, + ); + await exchangeTradeEmulator.transferFromAsync( + zrxAssetData, + senderAddress, + signedOrder.feeRecipientAddress, + takerFeeAmount, + TradeSide.Taker, + TransferType.Fee, + ); + } catch (err) { + throw new Error(RevertReasons.TransferFailed); + } } private static _validateRemainingFillAmountNotZeroOrThrow( takerAssetAmount: BigNumber, filledTakerTokenAmount: BigNumber, ): void { if (takerAssetAmount.eq(filledTakerTokenAmount)) { - throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); + throw new Error(RevertReasons.OrderUnfillable); } } private static _validateOrderNotExpiredOrThrow(expirationTimeSeconds: BigNumber): void { const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec(); if (expirationTimeSeconds.lessThan(currentUnixTimestampSec)) { - throw new Error(ExchangeContractErrs.OrderFillExpired); + throw new Error(RevertReasons.OrderUnfillable); } } constructor(orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher) { @@ -153,7 +158,7 @@ export class OrderValidationUtils { zrxAssetData: string, ): Promise { if (fillTakerAssetAmount.eq(0)) { - throw new Error(ExchangeContractErrs.OrderFillAmountZero); + throw new Error(RevertReasons.TakerAssetFillAmount); } const orderHash = orderHashUtils.getOrderHashHex(signedOrder); const isValid = await isValidSignatureAsync( @@ -171,7 +176,7 @@ export class OrderValidationUtils { filledTakerTokenAmount, ); if (signedOrder.takerAddress !== constants.NULL_ADDRESS && signedOrder.takerAddress !== takerAddress) { - throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); + throw new Error(RevertReasons.InvalidTaker); } OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationTimeSeconds); const remainingTakerTokenAmount = signedOrder.takerAssetAmount.minus(filledTakerTokenAmount); @@ -192,7 +197,7 @@ export class OrderValidationUtils { signedOrder.makerAssetAmount, ); if (wouldRoundingErrorOccur) { - throw new Error(ExchangeContractErrs.OrderFillRoundingError); + throw new Error(RevertReasons.RoundingError); } return filledTakerTokenAmount; } @@ -213,7 +218,7 @@ export class OrderValidationUtils { zrxAssetData, ); if (filledTakerTokenAmount !== fillTakerAssetAmount) { - throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount); + throw new Error(RevertReasons.OrderUnfillable); } } } -- cgit From 7b0f7c2e5c11ca03927eeeeada6d05994774fe86 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 25 Jun 2018 13:51:18 +0200 Subject: Remove no longer used method --- packages/order-utils/src/order_validation_utils.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/packages/order-utils/src/order_validation_utils.ts b/packages/order-utils/src/order_validation_utils.ts index d2449b183..ba7050ac7 100644 --- a/packages/order-utils/src/order_validation_utils.ts +++ b/packages/order-utils/src/order_validation_utils.ts @@ -32,22 +32,6 @@ export class OrderValidationUtils { const isError = errPercentageTimes1000000.gt(1000); return isError; } - public static validateCancelOrderThrowIfInvalid( - order: Order, - cancelTakerTokenAmount: BigNumber, - filledTakerTokenAmount: BigNumber, - ): void { - if (cancelTakerTokenAmount.eq(0)) { - throw new Error(ExchangeContractErrs.OrderCancelAmountZero); - } - if (order.takerAssetAmount.eq(filledTakerTokenAmount)) { - throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); - } - const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec(); - if (order.expirationTimeSeconds.lessThan(currentUnixTimestampSec)) { - throw new Error(ExchangeContractErrs.OrderCancelExpired); - } - } public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync( exchangeTradeEmulator: ExchangeTransferSimulator, signedOrder: SignedOrder, -- cgit From 71bac8c60b858dfd891fa12e7c76e4eae123ac15 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 25 Jun 2018 13:51:41 +0200 Subject: Update yarn.lock --- yarn.lock | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/yarn.lock b/yarn.lock index ff8f4aaf8..7da0ce975 100644 --- a/yarn.lock +++ b/yarn.lock @@ -310,6 +310,10 @@ version "2.2.48" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.48.tgz#3523b126a0b049482e1c3c11877460f76622ffab" +"@types/mocha@^5.2.2": + version "5.2.3" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.3.tgz#11f3a5629d67cd444fa6c94536576244e6a52ea9" + "@types/nock@^9.1.2": version "9.1.3" resolved "https://registry.yarnpkg.com/@types/nock/-/nock-9.1.3.tgz#1d445679375b9e25afd449dc56585f81729454e8" @@ -2875,6 +2879,17 @@ copyfiles@^1.2.0: noms "0.0.0" through2 "^2.0.1" +copyfiles@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.0.0.tgz#bbd78bb78e8fd6db5c67adf54249317b24560f2a" + dependencies: + glob "^7.0.5" + minimatch "^3.0.3" + mkdirp "^0.5.1" + noms "0.0.0" + through2 "^2.0.1" + yargs "^11.0.0" + core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" @@ -4038,6 +4053,13 @@ ethereum-common@^0.0.18: version "0.0.18" resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" +ethereum-types@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/ethereum-types/-/ethereum-types-0.0.2.tgz#6ef6faf46a24697cbf66b6c8a0ecf2095ce58c38" + dependencies: + "@types/node" "^8.0.53" + bignumber.js "~4.1.0" + ethereumjs-abi@0.6.5, ethereumjs-abi@^0.6.4, "ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git": version "0.6.5" resolved "git+https://github.com/ethereumjs/ethereumjs-abi.git#4ea2fdfed09e8f99117d9362d17c6b01b64a2bcf" @@ -10166,6 +10188,10 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" +run-s@^0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/run-s/-/run-s-0.0.0.tgz#599912be20c00ba7698655c9936d075d31b71754" + rustbn.js@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.1.2.tgz#979fa0f9562216dd667c9d2cd179ae5d13830eff" @@ -13115,6 +13141,23 @@ yargs@^10.0.3: y18n "^3.2.1" yargs-parser "^8.1.0" +yargs@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.0.0.tgz#c052931006c5eee74610e5fc0354bedfd08a201b" + dependencies: + cliui "^4.0.0" + decamelize "^1.1.1" + find-up "^2.1.0" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^9.0.2" + yargs@^4.7.1: version "4.8.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0" -- cgit From 71c9b98896f454945e41661aca514946004f2346 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 25 Jun 2018 14:23:35 +0200 Subject: Fix revertReason --- packages/order-utils/src/order_validation_utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/order-utils/src/order_validation_utils.ts b/packages/order-utils/src/order_validation_utils.ts index ba7050ac7..11a7a36c5 100644 --- a/packages/order-utils/src/order_validation_utils.ts +++ b/packages/order-utils/src/order_validation_utils.ts @@ -142,7 +142,7 @@ export class OrderValidationUtils { zrxAssetData: string, ): Promise { if (fillTakerAssetAmount.eq(0)) { - throw new Error(RevertReasons.TakerAssetFillAmount); + throw new Error(RevertReasons.InvalidTakerAmount); } const orderHash = orderHashUtils.getOrderHashHex(signedOrder); const isValid = await isValidSignatureAsync( -- cgit From 1a4e99431bf0cfc8c5db2235a935d62ebb8469b2 Mon Sep 17 00:00:00 2001 From: perissology Date: Mon, 25 Jun 2018 07:55:19 -0700 Subject: support ignoring entire contracts --- packages/sol-cov/src/ast_visitor.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/sol-cov/src/ast_visitor.ts b/packages/sol-cov/src/ast_visitor.ts index 49b7ac4bd..166cf13bd 100644 --- a/packages/sol-cov/src/ast_visitor.ts +++ b/packages/sol-cov/src/ast_visitor.ts @@ -23,11 +23,13 @@ export class ASTVisitor { private _modifiersStatementIds: number[] = []; private _statementMap: StatementMap = {}; private _locationByOffset: LocationByOffset; - private _ignoreRangesBeginingAt: number[]; + private _ignoreRangesBeginningAt: number[]; + // keep track of contract/function ranges that are to be ignored + // so we can also ignore any children nodes within the contract/function private _ignoreRangesWithin: Array<[number, number]> = []; - constructor(locationByOffset: LocationByOffset, ignoreRangesBeginingAt: number[] = []) { + constructor(locationByOffset: LocationByOffset, ignoreRangesBeginningAt: number[] = []) { this._locationByOffset = locationByOffset; - this._ignoreRangesBeginingAt = ignoreRangesBeginingAt; + this._ignoreRangesBeginningAt = ignoreRangesBeginningAt; } public getCollectedCoverageEntries(): CoverageEntriesDescription { const coverageEntriesDescription = { @@ -45,6 +47,11 @@ export class ASTVisitor { public FunctionDefinition(ast: Parser.FunctionDefinition): void { this._visitFunctionLikeDefinition(ast); } + public ContractDefinition(ast: Parser.ContractDefinition): void { + if (this._ignoreExpression(ast)) { + this._ignoreRangesWithin.push(ast.range as [number, number]); + } + } public ModifierDefinition(ast: Parser.ModifierDefinition): void { this._visitFunctionLikeDefinition(ast); } @@ -143,13 +150,10 @@ export class ASTVisitor { this._ignoreRangesWithin, ([rangeStart, rangeEnd]: [number, number]) => astStart >= rangeStart && astEnd <= rangeEnd, ); - return this._ignoreRangesBeginingAt.includes(astStart) || isRangeIgnored; + return this._ignoreRangesBeginningAt.includes(astStart) || isRangeIgnored; } private _visitFunctionLikeDefinition(ast: Parser.ModifierDefinition | Parser.FunctionDefinition): void { if (this._ignoreExpression(ast)) { - // we want to ignore everything within this function - // add this nodes range to the ignoreRangesWithin array - // so we can ignore any later nodes that are children of this node this._ignoreRangesWithin.push(ast.range as [number, number]); return; } -- cgit From 92cb9c3807917049b392d86160e7d88f73a5c7b4 Mon Sep 17 00:00:00 2001 From: perissology Date: Mon, 25 Jun 2018 08:18:02 -0700 Subject: add /*solcov ignore next*/ tests --- .../sol-cov/test/collect_coverage_entries_test.ts | 30 ++++++++++++++++++++++ .../test/fixtures/contracts/SolcovIgnore.sol | 22 ++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 packages/sol-cov/test/fixtures/contracts/SolcovIgnore.sol diff --git a/packages/sol-cov/test/collect_coverage_entries_test.ts b/packages/sol-cov/test/collect_coverage_entries_test.ts index f88f3b3c3..7832ec316 100644 --- a/packages/sol-cov/test/collect_coverage_entries_test.ts +++ b/packages/sol-cov/test/collect_coverage_entries_test.ts @@ -121,5 +121,35 @@ describe('Collect coverage entries', () => { const branchTypes = _.map(branchDescriptions, branchDescription => branchDescription.type); expect(branchTypes).to.be.deep.equal(['if', 'if', 'if', 'if', 'binary-expr', 'if']); }); + + it('correctly ignores all coverage entries for Ignore contract', () => { + const solcovIgnoreContractBaseName = 'SolcovIgnore.sol'; + const solcovIgnoreContractFileName = path.resolve( + __dirname, + 'fixtures/contracts', + solcovIgnoreContractBaseName, + ); + const solcovIgnoreContract = fs.readFileSync(solcovIgnoreContractFileName).toString(); + const coverageEntries = collectCoverageEntries(solcovIgnoreContract); + const fnIds = _.keys(coverageEntries.fnMap); + + expect(fnIds.length).to.be.equal(1); + expect(coverageEntries.fnMap[fnIds[0]].name).to.be.equal('set'); + // tslint:disable-next-line:custom-no-magic-numbers + expect(coverageEntries.fnMap[fnIds[0]].line).to.be.equal(6); + const setFunction = `function set(uint x) public { + /* solcov ignore next */ + storedData = x; + }`; + expect(utils.getRange(solcovIgnoreContract, coverageEntries.fnMap[fnIds[0]].loc)).to.be.equal(setFunction); + + expect(coverageEntries.branchMap).to.be.deep.equal({}); + const statementIds = _.keys(coverageEntries.statementMap); + expect(utils.getRange(solcovIgnoreContract, coverageEntries.statementMap[statementIds[0]])).to.be.equal( + setFunction, + ); + expect(statementIds.length).to.be.equal(1); + expect(coverageEntries.modifiersStatementIds.length).to.be.equal(0); + }); }); }); diff --git a/packages/sol-cov/test/fixtures/contracts/SolcovIgnore.sol b/packages/sol-cov/test/fixtures/contracts/SolcovIgnore.sol new file mode 100644 index 000000000..a7977ffb4 --- /dev/null +++ b/packages/sol-cov/test/fixtures/contracts/SolcovIgnore.sol @@ -0,0 +1,22 @@ +pragma solidity ^0.4.21; + +contract SolcovIgnore { + uint public storedData; + + function set(uint x) public { + /* solcov ignore next */ + storedData = x; + } + + /* solcov ignore next */ + function get() constant public returns (uint retVal) { + return storedData; + } +} + +/* solcov ignore next */ +contract Ignore { + function ignored() public returns (bool) { + return false; + } +} -- cgit From 178676ef8c5a32cdb557d04a0658d0967b4b8a75 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 25 Jun 2018 20:08:00 +0200 Subject: Fix lint errors --- packages/order-utils/src/exchange_transfer_simulator.ts | 3 +-- packages/order-utils/src/order_state_utils.ts | 1 - packages/order-utils/src/order_validation_utils.ts | 3 +-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/order-utils/src/exchange_transfer_simulator.ts b/packages/order-utils/src/exchange_transfer_simulator.ts index a70259fcd..58d122ee4 100644 --- a/packages/order-utils/src/exchange_transfer_simulator.ts +++ b/packages/order-utils/src/exchange_transfer_simulator.ts @@ -1,4 +1,4 @@ -import { AssetProxyId, ExchangeContractErrs } from '@0xproject/types'; +import { ExchangeContractErrs } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { AbstractBalanceAndProxyAllowanceLazyStore } from './abstract/abstract_balance_and_proxy_allowance_lazy_store'; @@ -90,7 +90,6 @@ export class ExchangeTransferSimulator { userAddress: string, amountInBaseUnits: BigNumber, ): Promise { - const assetProxyId = assetProxyUtils.decodeAssetDataId(assetData); const proxyAllowance = await this._store.getProxyAllowanceAsync(assetData, userAddress); // HACK: This code assumes that all tokens with an UNLIMITED_ALLOWANCE_IN_BASE_UNITS set, // are UnlimitedAllowanceTokens. This is however not true, it just so happens that all diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts index 3752fdadb..e3423ccf2 100644 --- a/packages/order-utils/src/order_state_utils.ts +++ b/packages/order-utils/src/order_state_utils.ts @@ -7,7 +7,6 @@ import { SignedOrder, } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; diff --git a/packages/order-utils/src/order_validation_utils.ts b/packages/order-utils/src/order_validation_utils.ts index 11a7a36c5..778556d6a 100644 --- a/packages/order-utils/src/order_validation_utils.ts +++ b/packages/order-utils/src/order_validation_utils.ts @@ -1,5 +1,4 @@ -import { Order, SignedOrder } from '@0xproject/types'; -import { RevertReasons } from '@0xproject/types'; +import { RevertReasons, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; -- cgit From 1134ff107542d9014b195b6dc6ce6040752c92e3 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 25 Jun 2018 20:17:00 +0200 Subject: Fix tslint issues --- packages/contracts/src/abstract/abstract_asset_wrapper.ts | 2 -- packages/contracts/src/utils/asset_wrapper.ts | 2 -- packages/contracts/src/utils/core_combinatorial_utils.ts | 14 ++------------ packages/contracts/src/utils/erc721_wrapper.ts | 3 +-- packages/contracts/test/exchange/combinatorial_tests.ts | 2 +- packages/contracts/test/exchange/core.ts | 1 - packages/contracts/test/exchange/fill_order.ts | 2 -- packages/order-utils/src/exchange_transfer_simulator.ts | 1 - packages/order-utils/src/order_state_utils.ts | 1 - 9 files changed, 4 insertions(+), 24 deletions(-) diff --git a/packages/contracts/src/abstract/abstract_asset_wrapper.ts b/packages/contracts/src/abstract/abstract_asset_wrapper.ts index 521335f18..0a0b42eea 100644 --- a/packages/contracts/src/abstract/abstract_asset_wrapper.ts +++ b/packages/contracts/src/abstract/abstract_asset_wrapper.ts @@ -1,5 +1,3 @@ -import { BigNumber } from '@0xproject/utils'; - export abstract class AbstractAssetWrapper { public abstract getProxyId(): number; } diff --git a/packages/contracts/src/utils/asset_wrapper.ts b/packages/contracts/src/utils/asset_wrapper.ts index 9c683054d..d5ce8cf1e 100644 --- a/packages/contracts/src/utils/asset_wrapper.ts +++ b/packages/contracts/src/utils/asset_wrapper.ts @@ -113,7 +113,6 @@ export class AssetWrapper { const isProxyApprovedForAll = await assetWrapper.isProxyApprovedForAllAsync( userAddress, erc721ProxyData.tokenAddress, - erc721ProxyData.tokenId, ); if (isProxyApprovedForAll) { return constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; @@ -169,7 +168,6 @@ export class AssetWrapper { const isProxyApprovedForAll = await erc721Wrapper.isProxyApprovedForAllAsync( userAddress, assetProxyData.tokenAddress, - assetProxyData.tokenId, ); if (!isProxyApprovedForAll && desiredAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) { const isApproved = true; diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index aea9a65e7..a2d2a500b 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -6,11 +6,11 @@ import { OrderStateUtils, OrderValidationUtils, } from '@0xproject/order-utils'; -import { AssetProxyId, Order, SignatureType, SignedOrder } from '@0xproject/types'; +import { AssetProxyId, SignatureType, SignedOrder } from '@0xproject/types'; import { BigNumber, errorUtils, logUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; -import { BlockParamLiteral, LogWithDecodedArgs, Provider, TxData } from 'ethereum-types'; +import { LogWithDecodedArgs, Provider, TxData } from 'ethereum-types'; import * as _ from 'lodash'; import 'make-promises-safe'; @@ -36,7 +36,6 @@ import { FeeRecipientAddressScenario, FillScenario, OrderAssetAmountScenario, - OrderScenario, TakerAssetFillAmountScenario, TakerScenario, TraderStateScenario, @@ -290,7 +289,6 @@ export class CoreCombinatorialUtils { fillScenario.takerStateScenario, signedOrder, takerAssetFillAmount, - balanceAndProxyAllowanceFetcher, ); // 5. If I fill it by X, what are the resulting balances/allowances/filled amounts expected? @@ -325,7 +323,6 @@ export class CoreCombinatorialUtils { takerAssetFillAmount, lazyStore, fillRevertReasonIfExists, - provider, ); } private async _fillOrderAndAssertOutcomeAsync( @@ -333,7 +330,6 @@ export class CoreCombinatorialUtils { takerAssetFillAmount: BigNumber, lazyStore: BalanceAndProxyAllowanceLazyStore, fillRevertReasonIfExists: string | undefined, - provider: Provider, ): Promise { if (!_.isUndefined(fillRevertReasonIfExists)) { return expectRevertReasonOrAlwaysFailingTransactionAsync( @@ -375,11 +371,6 @@ export class CoreCombinatorialUtils { signedOrder.makerAssetAmount, ); - const beforeMakerAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync( - makerAddress, - makerAssetData, - ); - // - Let's fill the order! const txReceipt = await this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { takerAssetFillAmount, @@ -544,7 +535,6 @@ export class CoreCombinatorialUtils { takerStateScenario: TraderStateScenario, signedOrder: SignedOrder, takerAssetFillAmount: BigNumber, - balanceAndProxyAllowanceFetcher: SimpleAssetBalanceAndProxyAllowanceFetcher, ): Promise { const makerAssetFillAmount = orderUtils.getPartialAmount( takerAssetFillAmount, diff --git a/packages/contracts/src/utils/erc721_wrapper.ts b/packages/contracts/src/utils/erc721_wrapper.ts index 6df9fa12e..e83e845a0 100644 --- a/packages/contracts/src/utils/erc721_wrapper.ts +++ b/packages/contracts/src/utils/erc721_wrapper.ts @@ -1,4 +1,4 @@ -import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { generatePseudoRandomSalt } from '@0xproject/order-utils'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Provider } from 'ethereum-types'; @@ -155,7 +155,6 @@ export class ERC721Wrapper { public async isProxyApprovedForAllAsync( userAddress: string, tokenAddress: string, - tokenId: BigNumber, ): Promise { this._validateProxyContractExistsOrThrow(); const tokenContract = this._getTokenContractFromAssetData(tokenAddress); diff --git a/packages/contracts/test/exchange/combinatorial_tests.ts b/packages/contracts/test/exchange/combinatorial_tests.ts index 754af9b08..f5f795bbe 100644 --- a/packages/contracts/test/exchange/combinatorial_tests.ts +++ b/packages/contracts/test/exchange/combinatorial_tests.ts @@ -5,7 +5,7 @@ import { chaiSetup } from '../../src/utils/chai_setup'; import { CoreCombinatorialUtils, coreCombinatorialUtilsFactoryAsync } from '../../src/utils/core_combinatorial_utils'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; -import { FillScenario, OrderScenario, TakerAssetFillAmountScenario } from '../../src/utils/types'; +import { FillScenario } from '../../src/utils/types'; chaiSetup.configure(); const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index 449bc5601..b9ef0a652 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -14,7 +14,6 @@ import { ERC721ProxyContract } from '../../src/generated_contract_wrappers/e_r_c import { CancelContractEventArgs, ExchangeContract, - FillContractEventArgs, } from '../../src/generated_contract_wrappers/exchange'; import { artifacts } from '../../src/utils/artifacts'; import { diff --git a/packages/contracts/test/exchange/fill_order.ts b/packages/contracts/test/exchange/fill_order.ts index 56d7510f4..6f57ad9f4 100644 --- a/packages/contracts/test/exchange/fill_order.ts +++ b/packages/contracts/test/exchange/fill_order.ts @@ -1,5 +1,4 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import * as _ from 'lodash'; import { chaiSetup } from '../../src/utils/chai_setup'; import { CoreCombinatorialUtils, coreCombinatorialUtilsFactoryAsync } from '../../src/utils/core_combinatorial_utils'; @@ -12,7 +11,6 @@ import { ExpirationTimeSecondsScenario, FeeRecipientAddressScenario, OrderAssetAmountScenario, - OrderScenario, TakerAssetFillAmountScenario, TakerScenario, } from '../../src/utils/types'; diff --git a/packages/order-utils/src/exchange_transfer_simulator.ts b/packages/order-utils/src/exchange_transfer_simulator.ts index 58d122ee4..72ed85406 100644 --- a/packages/order-utils/src/exchange_transfer_simulator.ts +++ b/packages/order-utils/src/exchange_transfer_simulator.ts @@ -2,7 +2,6 @@ import { ExchangeContractErrs } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { AbstractBalanceAndProxyAllowanceLazyStore } from './abstract/abstract_balance_and_proxy_allowance_lazy_store'; -import { assetProxyUtils } from './asset_proxy_utils'; import { constants } from './constants'; import { TradeSide, TransferType } from './types'; diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts index e3423ccf2..dd45a1298 100644 --- a/packages/order-utils/src/order_state_utils.ts +++ b/packages/order-utils/src/order_state_utils.ts @@ -10,7 +10,6 @@ import { BigNumber } from '@0xproject/utils'; import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; -import { constants } from './constants'; import { orderHashUtils } from './order_hash'; import { RemainingFillableCalculator } from './remaining_fillable_calculator'; import { utils } from './utils'; -- cgit From 8064914bb7f226c98d62357e29581238f0f765e4 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Mon, 25 Jun 2018 23:17:56 +0200 Subject: Prettier fixes --- packages/contracts/src/utils/erc721_wrapper.ts | 5 +---- packages/contracts/test/exchange/core.ts | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/contracts/src/utils/erc721_wrapper.ts b/packages/contracts/src/utils/erc721_wrapper.ts index e83e845a0..0ad2fc7d7 100644 --- a/packages/contracts/src/utils/erc721_wrapper.ts +++ b/packages/contracts/src/utils/erc721_wrapper.ts @@ -152,10 +152,7 @@ export class ERC721Wrapper { const isOwner = tokenOwner === userAddress; return isOwner; } - public async isProxyApprovedForAllAsync( - userAddress: string, - tokenAddress: string, - ): Promise { + public async isProxyApprovedForAllAsync(userAddress: string, tokenAddress: string): Promise { this._validateProxyContractExistsOrThrow(); const tokenContract = this._getTokenContractFromAssetData(tokenAddress); const operator = (this._proxyContract as ERC721ProxyContract).address; diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index b9ef0a652..63cbc155a 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -11,10 +11,7 @@ import { DummyERC20TokenContract } from '../../src/generated_contract_wrappers/d import { DummyERC721TokenContract } from '../../src/generated_contract_wrappers/dummy_e_r_c721_token'; import { ERC20ProxyContract } from '../../src/generated_contract_wrappers/e_r_c20_proxy'; import { ERC721ProxyContract } from '../../src/generated_contract_wrappers/e_r_c721_proxy'; -import { - CancelContractEventArgs, - ExchangeContract, -} from '../../src/generated_contract_wrappers/exchange'; +import { CancelContractEventArgs, ExchangeContract } from '../../src/generated_contract_wrappers/exchange'; import { artifacts } from '../../src/utils/artifacts'; import { expectRevertOrAlwaysFailingTransactionAsync, -- cgit From 53e2cda4c8d6698b5c4b69f510ad8764cb089306 Mon Sep 17 00:00:00 2001 From: fragosti Date: Fri, 22 Jun 2018 15:37:42 -0700 Subject: Remove existing networkId watching logic from BlockchainWatcher --- packages/website/ts/blockchain.ts | 6 ++--- packages/website/ts/blockchain_watcher.ts | 37 ++++++++++--------------------- 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 46a4d6629..11202eb64 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -229,7 +229,7 @@ export class Blockchain { shouldPollUserAddress, ); this._contractWrappers.setProvider(provider, this.networkId); - await this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceStateAsync(); + await this._blockchainWatcher.startEmittingUserBalanceStateAsync(); this._dispatcher.updateProviderType(ProviderType.Ledger); } public async updateProviderToInjectedAsync(): Promise { @@ -259,7 +259,7 @@ export class Blockchain { this._contractWrappers.setProvider(provider, this.networkId); await this.fetchTokenInformationAsync(); - await this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceStateAsync(); + await this._blockchainWatcher.startEmittingUserBalanceStateAsync(); this._dispatcher.updateProviderType(ProviderType.Injected); delete this._ledgerSubprovider; delete this._cachedProvider; @@ -816,7 +816,7 @@ export class Blockchain { this._userAddressIfExists = userAddresses[0]; this._dispatcher.updateUserAddress(this._userAddressIfExists); await this.fetchTokenInformationAsync(); - await this._blockchainWatcher.startEmittingNetworkConnectionAndUserBalanceStateAsync(); + await this._blockchainWatcher.startEmittingUserBalanceStateAsync(); await this._rehydrateStoreWithContractEventsAsync(); } private _updateProviderName(injectedWeb3: Web3): void { diff --git a/packages/website/ts/blockchain_watcher.ts b/packages/website/ts/blockchain_watcher.ts index c576db6ac..8ae5e7797 100644 --- a/packages/website/ts/blockchain_watcher.ts +++ b/packages/website/ts/blockchain_watcher.ts @@ -8,7 +8,7 @@ export class BlockchainWatcher { private _web3Wrapper: Web3Wrapper; private _prevNetworkId: number; private _shouldPollUserAddress: boolean; - private _watchNetworkAndBalanceIntervalId: NodeJS.Timer; + private _watchBalanceIntervalId: NodeJS.Timer; private _prevUserEtherBalanceInWei?: BigNumber; private _prevUserAddressIfExists: string; constructor( @@ -23,7 +23,7 @@ export class BlockchainWatcher { this._web3Wrapper = web3Wrapper; } public destroy(): void { - this._stopEmittingNetworkConnectionAndUserBalanceState(); + this._stopEmittingUserBalanceState(); // HACK: stop() is only available on providerEngine instances const provider = this._web3Wrapper.getProvider(); if (!_.isUndefined((provider as any).stop)) { @@ -34,36 +34,23 @@ export class BlockchainWatcher { public updatePrevUserAddress(userAddress: string): void { this._prevUserAddressIfExists = userAddress; } - public async startEmittingNetworkConnectionAndUserBalanceStateAsync(): Promise { - if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) { + public async startEmittingUserBalanceStateAsync(): Promise { + if (!_.isUndefined(this._watchBalanceIntervalId)) { return; // we are already emitting the state } this._prevUserEtherBalanceInWei = undefined; - this._dispatcher.updateNetworkId(this._prevNetworkId); - await this._updateNetworkAndBalanceAsync(); - this._watchNetworkAndBalanceIntervalId = intervalUtils.setAsyncExcludingInterval( - this._updateNetworkAndBalanceAsync.bind(this), + await this._updateBalanceAsync(); + this._watchBalanceIntervalId = intervalUtils.setAsyncExcludingInterval( + this._updateBalanceAsync.bind(this), 5000, (err: Error) => { logUtils.log(`Watching network and balances failed: ${err.stack}`); - this._stopEmittingNetworkConnectionAndUserBalanceState(); + this._stopEmittingUserBalanceState(); }, ); } - private async _updateNetworkAndBalanceAsync(): Promise { - // Check for network state changes + private async _updateBalanceAsync(): Promise { let prevNodeVersion: string; - let currentNetworkId; - try { - currentNetworkId = await this._web3Wrapper.getNetworkIdAsync(); - } catch (err) { - // Noop - } - if (currentNetworkId !== this._prevNetworkId) { - this._prevNetworkId = currentNetworkId; - this._dispatcher.updateNetworkId(currentNetworkId); - } - // Check for node version changes const currentNodeVersion = await this._web3Wrapper.getNodeVersionAsync(); if (currentNodeVersion !== prevNodeVersion) { @@ -99,9 +86,9 @@ export class BlockchainWatcher { this._dispatcher.updateUserWeiBalance(balanceInWei); } } - private _stopEmittingNetworkConnectionAndUserBalanceState(): void { - if (!_.isUndefined(this._watchNetworkAndBalanceIntervalId)) { - intervalUtils.clearAsyncExcludingInterval(this._watchNetworkAndBalanceIntervalId); + private _stopEmittingUserBalanceState(): void { + if (!_.isUndefined(this._watchBalanceIntervalId)) { + intervalUtils.clearAsyncExcludingInterval(this._watchBalanceIntervalId); } } } -- cgit From c4e2dcafa4bd5f4b905680e2c1b80d6ddb6ab6fb Mon Sep 17 00:00:00 2001 From: fragosti Date: Mon, 25 Jun 2018 16:26:34 -0700 Subject: Have basic network switching working --- packages/website/ts/blockchain.ts | 224 +++++++++++---------- packages/website/ts/blockchain_watcher.ts | 22 +- .../onboarding/portal_onboarding_flow.tsx | 11 +- packages/website/ts/components/portal/portal.tsx | 10 +- packages/website/ts/components/top_bar/top_bar.tsx | 1 - packages/website/ts/types.ts | 10 + 6 files changed, 158 insertions(+), 120 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 11202eb64..16ed8d03a 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -50,6 +50,8 @@ import { SideToAssetToken, Token, TokenByAddress, + InjectedProviderObservable, + InjectedProviderUpdate, } from 'ts/types'; import { backendClient } from 'ts/utils/backend_client'; import { configs } from 'ts/utils/configs'; @@ -79,9 +81,8 @@ export class Blockchain { private _dispatcher: Dispatcher; private _web3Wrapper?: Web3Wrapper; private _blockchainWatcher?: BlockchainWatcher; + private _injectedProviderObservable?: InjectedProviderObservable; private _userAddressIfExists: string; - private _cachedProvider: Provider; - private _cachedProviderNetworkId: number; private _ledgerSubprovider: LedgerSubprovider; private _defaultGasPrice: BigNumber; private static _getNameGivenProvider(provider: Provider): string { @@ -92,13 +93,62 @@ export class Blockchain { } return providerNameIfExists; } - private static async _getProviderAsync(injectedWeb3: Web3, networkIdIfExists: number): Promise { + private static _getInjectedWeb3(): any { + return (window as any).web3; + } + private static _getFallbackNetworkId(): number { + return configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_KOVAN; + } + private static async _getInjectedWeb3ProviderNetworkIdIfExistsAsync(): Promise { + // Hack: We need to know the networkId the injectedWeb3 is connected to (if it is defined) in + // order to properly instantiate the web3Wrapper. Since we must use the async call, we cannot + // retrieve it from within the web3Wrapper constructor. This is and should remain the only + // call to a web3 instance outside of web3Wrapper in the entire dapp. + // In addition, if the user has an injectedWeb3 instance that is disconnected from a backing + // Ethereum node, this call will throw. We need to handle this case gracefully + const injectedWeb3 = Blockchain._getInjectedWeb3(); + let networkIdIfExists: number; + if (!_.isUndefined(injectedWeb3)) { + try { + networkIdIfExists = _.parseInt(await promisify(injectedWeb3.version.getNetwork)()); + } catch (err) { + // Ignore error and proceed with networkId undefined + } + } + return networkIdIfExists; + } + private static async _getProviderAsync( + injectedWeb3: Web3, + networkIdIfExists: number, + userLedgerProvider: boolean = false, + ): Promise { const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); + const isNetworkIdDefined = !_.isUndefined(networkIdIfExists); const publicNodeUrlsIfExistsForNetworkId = configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists]; const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId); let provider; - if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) { + if (userLedgerProvider && isNetworkIdDefined) { + const isU2FSupported = await utils.isU2FSupportedAsync(); + if (!isU2FSupported) { + throw new Error('Cannot update providerType to LEDGER without U2F support'); + } + provider = new ProviderEngine(); + const ledgerWalletConfigs = { + networkId: networkIdIfExists, + ledgerEthereumClientFactoryAsync: ledgerEthereumBrowserClientFactoryAsync, + }; + const ledgerSubprovider = new LedgerSubprovider(ledgerWalletConfigs); + provider.addProvider(ledgerSubprovider); + provider.addProvider(new FilterSubprovider()); + const rpcSubproviders = _.map(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists], publicNodeUrl => { + return new RpcSubprovider({ + rpcUrl: publicNodeUrl, + }); + }); + provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[])); + provider.start(); + } else if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) { // We catch all requests involving a users account and send it to the injectedWeb3 // instance. All other requests go to the public hosted node. provider = new ProviderEngine(); @@ -120,7 +170,7 @@ export class Blockchain { // injected into their browser. provider = new ProviderEngine(); provider.addProvider(new FilterSubprovider()); - const networkId = configs.IS_MAINNET_ENABLED ? constants.NETWORK_ID_MAINNET : constants.NETWORK_ID_KOVAN; + const networkId = Blockchain._getFallbackNetworkId(); const rpcSubproviders = _.map(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], publicNodeUrl => { return new RpcSubprovider({ rpcUrl: publicNodeUrl, @@ -141,6 +191,7 @@ export class Blockchain { // tslint:disable-next-line:no-floating-promises this._onPageLoadInitFireAndForgetAsync(); } + // TODO: Investigate if we need this. public async networkIdUpdatedFireAndForgetAsync(newNetworkId: number): Promise { const isConnected = !_.isUndefined(newNetworkId); if (!isConnected) { @@ -185,84 +236,14 @@ export class Blockchain { this._ledgerSubprovider.setPath(path); } public async updateProviderToLedgerAsync(networkId: number): Promise { - utils.assert(!_.isUndefined(this._contractWrappers), 'Contract Wrappers must be instantiated.'); - - const isU2FSupported = await utils.isU2FSupportedAsync(); - if (!isU2FSupported) { - throw new Error('Cannot update providerType to LEDGER without U2F support'); - } - - // Cache injected provider so that we can switch the user back to it easily - if (_.isUndefined(this._cachedProvider)) { - this._cachedProvider = this._web3Wrapper.getProvider(); - this._cachedProviderNetworkId = this.networkId; - } - - this._blockchainWatcher.destroy(); - - delete this._userAddressIfExists; - this._dispatcher.updateUserAddress(undefined); // Clear old userAddress - - const provider = new ProviderEngine(); - const ledgerWalletConfigs = { - networkId, - ledgerEthereumClientFactoryAsync: ledgerEthereumBrowserClientFactoryAsync, - }; - this._ledgerSubprovider = new LedgerSubprovider(ledgerWalletConfigs); - provider.addProvider(this._ledgerSubprovider); - provider.addProvider(new FilterSubprovider()); - const rpcSubproviders = _.map(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], publicNodeUrl => { - return new RpcSubprovider({ - rpcUrl: publicNodeUrl, - }); - }); - provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[])); - provider.start(); - this.networkId = networkId; - this._dispatcher.updateNetworkId(this.networkId); const shouldPollUserAddress = false; - this._web3Wrapper = new Web3Wrapper(provider); - this._blockchainWatcher = new BlockchainWatcher( - this._dispatcher, - this._web3Wrapper, - this.networkId, - shouldPollUserAddress, - ); - this._contractWrappers.setProvider(provider, this.networkId); - await this._blockchainWatcher.startEmittingUserBalanceStateAsync(); - this._dispatcher.updateProviderType(ProviderType.Ledger); + const useLedgerProvider = true; + await this._resetOrInitializeAsync(networkId, shouldPollUserAddress, useLedgerProvider); } public async updateProviderToInjectedAsync(): Promise { - utils.assert(!_.isUndefined(this._contractWrappers), 'Contract Wrappers must be instantiated.'); - - if (_.isUndefined(this._cachedProvider)) { - return; // Going from injected to injected, so we noop - } - - this._blockchainWatcher.destroy(); - - const provider = this._cachedProvider; - this.networkId = this._cachedProviderNetworkId; - const shouldPollUserAddress = true; - this._web3Wrapper = new Web3Wrapper(provider); - this._blockchainWatcher = new BlockchainWatcher( - this._dispatcher, - this._web3Wrapper, - this.networkId, - shouldPollUserAddress, - ); - - const userAddresses = await this._web3Wrapper.getAvailableAddressesAsync(); - this._userAddressIfExists = userAddresses[0]; - - this._contractWrappers.setProvider(provider, this.networkId); - - await this.fetchTokenInformationAsync(); - await this._blockchainWatcher.startEmittingUserBalanceStateAsync(); - this._dispatcher.updateProviderType(ProviderType.Injected); - delete this._ledgerSubprovider; - delete this._cachedProvider; + const userLedgerProvider = false; + await this._resetOrInitializeAsync(this.networkId, shouldPollUserAddress, userLedgerProvider); } public async setProxyAllowanceAsync(token: Token, amountInBaseUnits: BigNumber): Promise { utils.assert(this.isValidAddress(token.address), BlockchainCallErrs.TokenAddressIsInvalid); @@ -632,6 +613,19 @@ export class Blockchain { private _doesUserAddressExist(): boolean { return !_.isUndefined(this._userAddressIfExists); } + private async _handleInjectedProviderUpdateAsync(update: InjectedProviderUpdate): Promise { + if (update.networkVersion === 'loading') { + // Who comes up with this stuff. + return; + } + const updatedNetworkId = _.parseInt(update.networkVersion); + if (this.networkId === updatedNetworkId) { + return; + } + const shouldPollUserAddress = true; + const useLedgerProvider = false; + await this._resetOrInitializeAsync(updatedNetworkId, shouldPollUserAddress, useLedgerProvider); + } private async _rehydrateStoreWithContractEventsAsync(): Promise { // Ensure we are only ever listening to one set of events this._stopWatchingExchangeLogFillEvents(); @@ -774,36 +768,37 @@ export class Blockchain { } private async _onPageLoadInitFireAndForgetAsync(): Promise { await utils.onPageLoadAsync(); // wait for page to load - - // Hack: We need to know the networkId the injectedWeb3 is connected to (if it is defined) in - // order to properly instantiate the web3Wrapper. Since we must use the async call, we cannot - // retrieve it from within the web3Wrapper constructor. This is and should remain the only - // call to a web3 instance outside of web3Wrapper in the entire dapp. - // In addition, if the user has an injectedWeb3 instance that is disconnected from a backing - // Ethereum node, this call will throw. We need to handle this case gracefully - const injectedWeb3 = (window as any).web3; - let networkIdIfExists: number; - if (!_.isUndefined(injectedWeb3)) { - try { - networkIdIfExists = _.parseInt(await promisify(injectedWeb3.version.getNetwork)()); - } catch (err) { - // Ignore error and proceed with networkId undefined + const networkIdIfExists = await Blockchain._getInjectedWeb3ProviderNetworkIdIfExistsAsync(); + this.networkId = !_.isUndefined(networkIdIfExists) ? networkIdIfExists : Blockchain._getFallbackNetworkId(); + const injectedWeb3 = Blockchain._getInjectedWeb3(); + if (injectedWeb3) { + const injectedProviderObservable = injectedWeb3.currentProvider.publicConfigStore; + if (injectedProviderObservable && !this._injectedProviderObservable) { + this._injectedProviderObservable = injectedProviderObservable; + this._injectedProviderObservable.subscribe(this._handleInjectedProviderUpdateAsync.bind(this)); } } - - const provider = await Blockchain._getProviderAsync(injectedWeb3, networkIdIfExists); - this.networkId = !_.isUndefined(networkIdIfExists) - ? networkIdIfExists - : configs.IS_MAINNET_ENABLED - ? constants.NETWORK_ID_MAINNET - : constants.NETWORK_ID_KOVAN; - this._dispatcher.updateNetworkId(this.networkId); - const zeroExConfigs = { - networkId: this.networkId, - }; - this._contractWrappers = new ContractWrappers(provider, zeroExConfigs); - this._updateProviderName(injectedWeb3); const shouldPollUserAddress = true; + const shouldUseLedger = false; + await this._resetOrInitializeAsync(this.networkId, shouldPollUserAddress, shouldUseLedger); + } + private async _resetOrInitializeAsync( + networkId: number, + shouldPollUserAddress: boolean = false, + useLedgerProvider: boolean = false, + ): Promise { + this.networkId = networkId; + this._dispatcher.updateNetworkId(networkId); + const injectedWeb3 = Blockchain._getInjectedWeb3(); + const provider = await Blockchain._getProviderAsync(injectedWeb3, networkId, useLedgerProvider); + // if (!_.isUndefined(this._contractWrappers)) { + // this._contractWrappers.setProvider(provider, networkId); + // } else { + // } + this._contractWrappers = new ContractWrappers(provider, { networkId }); + if (!_.isUndefined(this._blockchainWatcher)) { + this._blockchainWatcher.destroy(); + } this._web3Wrapper = new Web3Wrapper(provider); this._blockchainWatcher = new BlockchainWatcher( this._dispatcher, @@ -811,10 +806,19 @@ export class Blockchain { this.networkId, shouldPollUserAddress, ); - - const userAddresses = await this._web3Wrapper.getAvailableAddressesAsync(); - this._userAddressIfExists = userAddresses[0]; - this._dispatcher.updateUserAddress(this._userAddressIfExists); + if (useLedgerProvider) { + // TODO: why? + delete this._userAddressIfExists; + this._dispatcher.updateUserAddress(undefined); + this._dispatcher.updateProviderType(ProviderType.Ledger); + } else { + delete this._ledgerSubprovider; + const userAddresses = await this._web3Wrapper.getAvailableAddressesAsync(); + this._userAddressIfExists = userAddresses[0]; + this._dispatcher.updateUserAddress(this._userAddressIfExists); + this._updateProviderName(injectedWeb3); + } + // TOOD: should not call this in ledger case? await this.fetchTokenInformationAsync(); await this._blockchainWatcher.startEmittingUserBalanceStateAsync(); await this._rehydrateStoreWithContractEventsAsync(); diff --git a/packages/website/ts/blockchain_watcher.ts b/packages/website/ts/blockchain_watcher.ts index 8ae5e7797..5d029d4f1 100644 --- a/packages/website/ts/blockchain_watcher.ts +++ b/packages/website/ts/blockchain_watcher.ts @@ -7,6 +7,7 @@ export class BlockchainWatcher { private _dispatcher: Dispatcher; private _web3Wrapper: Web3Wrapper; private _prevNetworkId: number; + private _isWatchingNetworkId: boolean = false; private _shouldPollUserAddress: boolean; private _watchBalanceIntervalId: NodeJS.Timer; private _prevUserEtherBalanceInWei?: BigNumber; @@ -18,8 +19,8 @@ export class BlockchainWatcher { shouldPollUserAddress: boolean, ) { this._dispatcher = dispatcher; - this._prevNetworkId = networkIdIfExists; this._shouldPollUserAddress = shouldPollUserAddress; + this._prevNetworkId = networkIdIfExists; this._web3Wrapper = web3Wrapper; } public destroy(): void { @@ -34,6 +35,16 @@ export class BlockchainWatcher { public updatePrevUserAddress(userAddress: string): void { this._prevUserAddressIfExists = userAddress; } + // public async startEmittingInjectedProviderNetworkIdAsync(injectedProvider: any): Promise { + // if (this._isWatchingNetworkId) { + // return; // we are already watching the network id + // } + // const observable = injectedProvider.publicConfigStore; + // if (observable && observable.subscribe) { + // observable.subscribe(this._handleInjectedProviderUpdate.bind(this)); + // this._isWatchingNetworkId = true; + // } + // } public async startEmittingUserBalanceStateAsync(): Promise { if (!_.isUndefined(this._watchBalanceIntervalId)) { return; // we are already emitting the state @@ -49,6 +60,15 @@ export class BlockchainWatcher { }, ); } + // private _handleInjectedProviderUpdate(update: InjectedProviderUpdate): void { + // const updatedNetworkId = _.parseInt(update.networkVersion); + // if (this._prevNetworkId === updatedNetworkId) { + // return; + // } + // this._prevNetworkId = updatedNetworkId; + // this._dispatcher.updateNetworkId(updatedNetworkId); + // this._updateBalanceAsync(); + // } private async _updateBalanceAsync(): Promise { let prevNodeVersion: string; // Check for node version changes diff --git a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx index 10d4af30e..35b51140d 100644 --- a/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx +++ b/packages/website/ts/components/onboarding/portal_onboarding_flow.tsx @@ -159,9 +159,11 @@ class PlainPortalOnboardingFlow extends React.Component new BigNumber(0) && zrxTokenAllowance > new BigNumber(0); + const ethTokenState = this.props.trackedTokenStateByAddress[ethToken.address]; + const zrxTokenState = this.props.trackedTokenStateByAddress[zrxToken.address]; + if (ethTokenState && zrxTokenState) { + return ethTokenState.allowance > new BigNumber(0) && zrxTokenState.allowance > new BigNumber(0); + } } return false; } @@ -222,6 +224,9 @@ class PlainPortalOnboardingFlow extends React.Component { this.props.dispatcher.resetState(); } public componentDidUpdate(prevProps: PortalProps): void { - if (!prevProps.blockchainIsLoaded && this.props.blockchainIsLoaded) { - const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); - // tslint:disable-next-line:no-floating-promises - this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses); - } + // if (!prevProps.blockchainIsLoaded && this.props.blockchainIsLoaded) { + // const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); + // // tslint:disable-next-line:no-floating-promises + // this._fetchBalancesAndAllowancesAsync(trackedTokenAddresses); + // } } public componentWillReceiveProps(nextProps: PortalProps): void { if (nextProps.networkId !== this.state.prevNetworkId) { diff --git a/packages/website/ts/components/top_bar/top_bar.tsx b/packages/website/ts/components/top_bar/top_bar.tsx index 537edc7bb..85578b116 100644 --- a/packages/website/ts/components/top_bar/top_bar.tsx +++ b/packages/website/ts/components/top_bar/top_bar.tsx @@ -58,7 +58,6 @@ const styles: Styles = { width: '100%', position: 'relative', top: 0, - zIndex: zIndex.topBar, paddingBottom: 1, }, bottomBar: { diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index d00154652..411fc2233 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -488,6 +488,16 @@ export enum Providers { Mist = 'MIST', } +export interface InjectedProviderUpdate { + selectedAddress: string; + networkVersion: string; +} + +export interface InjectedProviderObservable { + subscribe(updateHandler: (update: InjectedProviderUpdate) => void): void; + unsubscribe(updateHandler: (update: InjectedProviderUpdate) => void): void; +} + export interface TimestampMsRange { startTimestampMs: number; endTimestampMs: number; -- cgit From fb03003b3a9e7beb1054a50bad4320e60ca52e04 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 26 Jun 2018 07:50:24 +0200 Subject: Fix inconsistency between contract and reference implementation of order validation logic --- packages/contracts/test/exchange/fill_order.ts | 2 ++ packages/order-utils/src/order_validation_utils.ts | 3 +++ 2 files changed, 5 insertions(+) diff --git a/packages/contracts/test/exchange/fill_order.ts b/packages/contracts/test/exchange/fill_order.ts index 6f57ad9f4..60fe777c3 100644 --- a/packages/contracts/test/exchange/fill_order.ts +++ b/packages/contracts/test/exchange/fill_order.ts @@ -123,6 +123,7 @@ describe('FillOrder Tests', () => { ...defaultFillScenario.orderScenario, makerAssetAmountScenario: OrderAssetAmountScenario.Zero, }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); @@ -134,6 +135,7 @@ describe('FillOrder Tests', () => { ...defaultFillScenario.orderScenario, takerAssetAmountScenario: OrderAssetAmountScenario.Zero, }, + takerAssetFillAmountScenario: TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount, }; await coreCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario); }); diff --git a/packages/order-utils/src/order_validation_utils.ts b/packages/order-utils/src/order_validation_utils.ts index 778556d6a..fb5143d03 100644 --- a/packages/order-utils/src/order_validation_utils.ts +++ b/packages/order-utils/src/order_validation_utils.ts @@ -143,6 +143,9 @@ export class OrderValidationUtils { if (fillTakerAssetAmount.eq(0)) { throw new Error(RevertReasons.InvalidTakerAmount); } + if (signedOrder.makerAssetAmount.eq(0) || signedOrder.takerAssetAmount.eq(0)) { + throw new Error(RevertReasons.OrderUnfillable); + } const orderHash = orderHashUtils.getOrderHashHex(signedOrder); const isValid = await isValidSignatureAsync( provider, -- cgit From 105b9273975b34d46dc98254846d2b31990d9d9a Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 26 Jun 2018 08:43:37 +0200 Subject: Fix merge issues --- packages/contracts/src/utils/asset_wrapper.ts | 19 ++++++++++--------- .../contracts/src/utils/core_combinatorial_utils.ts | 2 +- packages/contracts/src/utils/erc20_wrapper.ts | 6 +++--- packages/contracts/src/utils/erc721_wrapper.ts | 6 +++--- packages/contracts/test/asset_proxy/proxies.ts | 5 ++++- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/contracts/src/utils/asset_wrapper.ts b/packages/contracts/src/utils/asset_wrapper.ts index d5ce8cf1e..a7f91f413 100644 --- a/packages/contracts/src/utils/asset_wrapper.ts +++ b/packages/contracts/src/utils/asset_wrapper.ts @@ -1,4 +1,5 @@ import { assetProxyUtils } from '@0xproject/order-utils'; +import { AssetProxyId } from '@0xproject/types'; import { BigNumber, errorUtils } from '@0xproject/utils'; import * as _ from 'lodash'; @@ -9,7 +10,7 @@ import { ERC20Wrapper } from './erc20_wrapper'; import { ERC721Wrapper } from './erc721_wrapper'; interface ProxyIdToAssetWrappers { - [proxyId: number]: AbstractAssetWrapper; + [proxyId: string]: AbstractAssetWrapper; } /** @@ -28,12 +29,12 @@ export class AssetWrapper { public async getBalanceAsync(userAddress: string, assetData: string): Promise { const proxyId = assetProxyUtils.decodeAssetDataId(assetData); switch (proxyId) { - case constants.ERC20_PROXY_ID: { + case AssetProxyId.ERC20: { const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; const balance = await erc20Wrapper.getBalanceAsync(userAddress, assetData); return balance; } - case constants.ERC721_PROXY_ID: { + case AssetProxyId.ERC721: { const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper; const assetProxyData = assetProxyUtils.decodeERC721AssetData(assetData); const isOwner = await assetWrapper.isOwnerAsync( @@ -51,12 +52,12 @@ export class AssetWrapper { public async setBalanceAsync(userAddress: string, assetData: string, desiredBalance: BigNumber): Promise { const proxyId = assetProxyUtils.decodeAssetDataId(assetData); switch (proxyId) { - case constants.ERC20_PROXY_ID: { + case AssetProxyId.ERC20: { const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; await erc20Wrapper.setBalanceAsync(userAddress, assetData, desiredBalance); return; } - case constants.ERC721_PROXY_ID: { + case AssetProxyId.ERC721: { if (!desiredBalance.eq(0) && !desiredBalance.eq(1)) { throw new Error(`Balance for ERC721 token can only be set to 0 or 1. Got: ${desiredBalance}`); } @@ -102,12 +103,12 @@ export class AssetWrapper { public async getProxyAllowanceAsync(userAddress: string, assetData: string): Promise { const proxyId = assetProxyUtils.decodeAssetDataId(assetData); switch (proxyId) { - case constants.ERC20_PROXY_ID: { + case AssetProxyId.ERC20: { const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; const allowance = await erc20Wrapper.getProxyAllowanceAsync(userAddress, assetData); return allowance; } - case constants.ERC721_PROXY_ID: { + case AssetProxyId.ERC721: { const assetWrapper = this._proxyIdToAssetWrappers[proxyId] as ERC721Wrapper; const erc721ProxyData = assetProxyUtils.decodeERC721AssetData(assetData); const isProxyApprovedForAll = await assetWrapper.isProxyApprovedForAllAsync( @@ -136,12 +137,12 @@ export class AssetWrapper { ): Promise { const proxyId = assetProxyUtils.decodeAssetDataId(assetData); switch (proxyId) { - case constants.ERC20_PROXY_ID: { + case AssetProxyId.ERC20: { const erc20Wrapper = this._proxyIdToAssetWrappers[proxyId] as ERC20Wrapper; await erc20Wrapper.setAllowanceAsync(userAddress, assetData, desiredAllowance); return; } - case constants.ERC721_PROXY_ID: { + case AssetProxyId.ERC721: { if ( !desiredAllowance.eq(0) && !desiredAllowance.eq(1) && diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index a2d2a500b..a775c619f 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -515,7 +515,7 @@ export class CoreCombinatorialUtils { const takerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.takerAssetData); const makerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.makerAssetData); const isEitherAssetERC721 = - takerAssetProxyId === constants.ERC721_PROXY_ID || makerAssetProxyId === constants.ERC721_PROXY_ID; + takerAssetProxyId === AssetProxyId.ERC721 || makerAssetProxyId === AssetProxyId.ERC721; if (isEitherAssetERC721) { throw new Error( 'Cannot test `TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount` together with ERC721 assets since orders involving ERC721 must always be filled exactly.', diff --git a/packages/contracts/src/utils/erc20_wrapper.ts b/packages/contracts/src/utils/erc20_wrapper.ts index 6dd717f4a..652f29726 100644 --- a/packages/contracts/src/utils/erc20_wrapper.ts +++ b/packages/contracts/src/utils/erc20_wrapper.ts @@ -19,7 +19,7 @@ export class ERC20Wrapper { private _provider: Provider; private _dummyTokenContracts: DummyERC20TokenContract[]; private _proxyContract?: ERC20ProxyContract; - private _proxyIdIfExists?: number; + private _proxyIdIfExists?: string; constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { this._dummyTokenContracts = []; this._web3Wrapper = new Web3Wrapper(provider); @@ -55,9 +55,9 @@ export class ERC20Wrapper { this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync(); return this._proxyContract; } - public getProxyId(): number { + public getProxyId(): string { this._validateProxyContractExistsOrThrow(); - return this._proxyIdIfExists as number; + return this._proxyIdIfExists as string; } public async setBalancesAndAllowancesAsync(): Promise { this._validateDummyTokenContractsExistOrThrow(); diff --git a/packages/contracts/src/utils/erc721_wrapper.ts b/packages/contracts/src/utils/erc721_wrapper.ts index 0ad2fc7d7..309135ae4 100644 --- a/packages/contracts/src/utils/erc721_wrapper.ts +++ b/packages/contracts/src/utils/erc721_wrapper.ts @@ -19,7 +19,7 @@ export class ERC721Wrapper { private _provider: Provider; private _dummyTokenContracts: DummyERC721TokenContract[]; private _proxyContract?: ERC721ProxyContract; - private _proxyIdIfExists?: number; + private _proxyIdIfExists?: string; private _initialTokenIdsByOwner: ERC721TokenIdsByOwner = {}; constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) { this._web3Wrapper = new Web3Wrapper(provider); @@ -51,9 +51,9 @@ export class ERC721Wrapper { this._proxyIdIfExists = await this._proxyContract.getProxyId.callAsync(); return this._proxyContract; } - public getProxyId(): number { + public getProxyId(): string { this._validateProxyContractExistsOrThrow(); - return this._proxyIdIfExists as number; + return this._proxyIdIfExists as string; } public async setBalancesAndAllowancesAsync(): Promise { this._validateDummyTokenContractsExistOrThrow(); diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts index 7960e46d2..5f4c5b597 100644 --- a/packages/contracts/test/asset_proxy/proxies.ts +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -62,7 +62,10 @@ describe('Asset Transfer Proxies', () => { erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); - [zrxToken] = await erc20Wrapper.deployDummyTokensAsync(); + [zrxToken] = await erc20Wrapper.deployDummyTokensAsync( + constants.NUM_DUMMY_ERC20_TO_DEPLOY, + constants.DUMMY_TOKEN_DECIMALS, + ); erc20Proxy = await erc20Wrapper.deployProxyAsync(); await erc20Wrapper.setBalancesAndAllowancesAsync(); await web3Wrapper.awaitTransactionSuccessAsync( -- cgit From 7967ebed574f46553c135da4486e0759bef499c8 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 26 Jun 2018 08:51:40 +0200 Subject: Rename to singular --- packages/contracts/test/asset_proxy/proxies.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts index 5f4c5b597..7e8e69f4e 100644 --- a/packages/contracts/test/asset_proxy/proxies.ts +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -1,6 +1,6 @@ import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; -import { RevertReason } from '@0xproject/types'; +import { RevertReasons } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; import { LogWithDecodedArgs } from 'ethereum-types'; @@ -173,7 +173,7 @@ describe('Asset Transfer Proxies', () => { transferAmount, { from: exchangeAddress }, ), - RevertReason.TransferFailed, + RevertReasons.TransferFailed, ); }); @@ -187,7 +187,7 @@ describe('Asset Transfer Proxies', () => { erc20Proxy.transferFrom.sendTransactionAsync(encodedAssetData, makerAddress, takerAddress, amount, { from: notAuthorized, }), - RevertReason.SenderNotAuthorized, + RevertReasons.SenderNotAuthorized, ); }); }); @@ -239,7 +239,7 @@ describe('Asset Transfer Proxies', () => { erc20Proxy.batchTransferFrom.sendTransactionAsync(assetData, fromAddresses, toAddresses, amounts, { from: notAuthorized, }), - RevertReason.SenderNotAuthorized, + RevertReasons.SenderNotAuthorized, ); }); }); @@ -381,7 +381,7 @@ describe('Asset Transfer Proxies', () => { amount, { from: exchangeAddress }, ), - RevertReason.InvalidAmount, + RevertReasons.InvalidAmount, ); }); @@ -401,7 +401,7 @@ describe('Asset Transfer Proxies', () => { amount, { from: exchangeAddress }, ), - RevertReason.InvalidAmount, + RevertReasons.InvalidAmount, ); }); @@ -421,7 +421,7 @@ describe('Asset Transfer Proxies', () => { erc20Proxy.transferFrom.sendTransactionAsync(encodedAssetData, makerAddress, takerAddress, amount, { from: exchangeAddress, }), - RevertReason.TransferFailed, + RevertReasons.TransferFailed, ); }); @@ -438,7 +438,7 @@ describe('Asset Transfer Proxies', () => { amount, { from: notAuthorized }, ), - RevertReason.SenderNotAuthorized, + RevertReasons.SenderNotAuthorized, ); }); }); @@ -493,7 +493,7 @@ describe('Asset Transfer Proxies', () => { erc721Proxy.batchTransferFrom.sendTransactionAsync(assetData, fromAddresses, toAddresses, amounts, { from: notAuthorized, }), - RevertReason.SenderNotAuthorized, + RevertReasons.SenderNotAuthorized, ); }); }); -- cgit From 6dc852774e23aa38c66188100c31ba42667620e8 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 26 Jun 2018 08:52:25 +0200 Subject: Update AbstractAssetWrapper --- packages/contracts/src/abstract/abstract_asset_wrapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/src/abstract/abstract_asset_wrapper.ts b/packages/contracts/src/abstract/abstract_asset_wrapper.ts index 0a0b42eea..4b56a8502 100644 --- a/packages/contracts/src/abstract/abstract_asset_wrapper.ts +++ b/packages/contracts/src/abstract/abstract_asset_wrapper.ts @@ -1,3 +1,3 @@ export abstract class AbstractAssetWrapper { - public abstract getProxyId(): number; + public abstract getProxyId(): string; } -- cgit From 2a82807be4099460173ce885867896a8755091b2 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 26 Jun 2018 19:16:57 +0200 Subject: Fix type issue --- packages/contracts/src/utils/core_combinatorial_utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts index a775c619f..becf19c06 100644 --- a/packages/contracts/src/utils/core_combinatorial_utils.ts +++ b/packages/contracts/src/utils/core_combinatorial_utils.ts @@ -6,7 +6,7 @@ import { OrderStateUtils, OrderValidationUtils, } from '@0xproject/order-utils'; -import { AssetProxyId, SignatureType, SignedOrder } from '@0xproject/types'; +import { AssetProxyId, RevertReason, SignatureType, SignedOrder } from '@0xproject/types'; import { BigNumber, errorUtils, logUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; @@ -329,7 +329,7 @@ export class CoreCombinatorialUtils { signedOrder: SignedOrder, takerAssetFillAmount: BigNumber, lazyStore: BalanceAndProxyAllowanceLazyStore, - fillRevertReasonIfExists: string | undefined, + fillRevertReasonIfExists: RevertReason | undefined, ): Promise { if (!_.isUndefined(fillRevertReasonIfExists)) { return expectRevertReasonOrAlwaysFailingTransactionAsync( -- cgit From f70c1ff009385266d694269c2c4dd0e9fb38dc46 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 26 Jun 2018 11:01:50 -0700 Subject: Ledger to metamask to ledger working --- packages/website/ts/blockchain.ts | 51 ++++++++++++++---------- packages/website/ts/blockchain_watcher.ts | 19 --------- packages/website/ts/components/portal/portal.tsx | 7 ++-- 3 files changed, 35 insertions(+), 42 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 16ed8d03a..d55cb6771 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -121,19 +121,18 @@ export class Blockchain { injectedWeb3: Web3, networkIdIfExists: number, userLedgerProvider: boolean = false, - ): Promise { + ): Promise<[Provider, LedgerSubprovider]> { const doesInjectedWeb3Exist = !_.isUndefined(injectedWeb3); const isNetworkIdDefined = !_.isUndefined(networkIdIfExists); const publicNodeUrlsIfExistsForNetworkId = configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkIdIfExists]; const isPublicNodeAvailableForNetworkId = !_.isUndefined(publicNodeUrlsIfExistsForNetworkId); - let provider; if (userLedgerProvider && isNetworkIdDefined) { const isU2FSupported = await utils.isU2FSupportedAsync(); if (!isU2FSupported) { throw new Error('Cannot update providerType to LEDGER without U2F support'); } - provider = new ProviderEngine(); + const provider = new ProviderEngine(); const ledgerWalletConfigs = { networkId: networkIdIfExists, ledgerEthereumClientFactoryAsync: ledgerEthereumBrowserClientFactoryAsync, @@ -148,10 +147,11 @@ export class Blockchain { }); provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[])); provider.start(); + return [provider, ledgerSubprovider]; } else if (doesInjectedWeb3Exist && isPublicNodeAvailableForNetworkId) { // We catch all requests involving a users account and send it to the injectedWeb3 // instance. All other requests go to the public hosted node. - provider = new ProviderEngine(); + const provider = new ProviderEngine(); provider.addProvider(new InjectedWeb3Subprovider(injectedWeb3.currentProvider)); provider.addProvider(new FilterSubprovider()); const rpcSubproviders = _.map(publicNodeUrlsIfExistsForNetworkId, publicNodeUrl => { @@ -161,14 +161,15 @@ export class Blockchain { }); provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[])); provider.start(); + return [provider, undefined]; } else if (doesInjectedWeb3Exist) { // Since no public node for this network, all requests go to injectedWeb3 instance - provider = injectedWeb3.currentProvider; + return [injectedWeb3.currentProvider, undefined]; } else { // If no injectedWeb3 instance, all requests fallback to our public hosted mainnet/testnet node // We do this so that users can still browse the 0x Portal DApp even if they do not have web3 // injected into their browser. - provider = new ProviderEngine(); + const provider = new ProviderEngine(); provider.addProvider(new FilterSubprovider()); const networkId = Blockchain._getFallbackNetworkId(); const rpcSubproviders = _.map(configs.PUBLIC_NODE_URLS_BY_NETWORK_ID[networkId], publicNodeUrl => { @@ -178,9 +179,8 @@ export class Blockchain { }); provider.addProvider(new RedundantSubprovider(rpcSubproviders as Subprovider[])); provider.start(); + return [provider, undefined]; } - - return provider; } constructor(dispatcher: Dispatcher) { this._dispatcher = dispatcher; @@ -243,6 +243,9 @@ export class Blockchain { public async updateProviderToInjectedAsync(): Promise { const shouldPollUserAddress = true; const userLedgerProvider = false; + this._dispatcher.updateBlockchainIsLoaded(false); + // We don't want to be out of sync with the network metamask declares. + const networkId = await Blockchain._getInjectedWeb3ProviderNetworkIdIfExistsAsync(); await this._resetOrInitializeAsync(this.networkId, shouldPollUserAddress, userLedgerProvider); } public async setProxyAllowanceAsync(token: Token, amountInBaseUnits: BigNumber): Promise { @@ -614,8 +617,7 @@ export class Blockchain { return !_.isUndefined(this._userAddressIfExists); } private async _handleInjectedProviderUpdateAsync(update: InjectedProviderUpdate): Promise { - if (update.networkVersion === 'loading') { - // Who comes up with this stuff. + if (update.networkVersion === 'loading' || !_.isUndefined(this._ledgerSubprovider)) { return; } const updatedNetworkId = _.parseInt(update.networkVersion); @@ -778,6 +780,7 @@ export class Blockchain { this._injectedProviderObservable.subscribe(this._handleInjectedProviderUpdateAsync.bind(this)); } } + this._updateProviderName(injectedWeb3); const shouldPollUserAddress = true; const shouldUseLedger = false; await this._resetOrInitializeAsync(this.networkId, shouldPollUserAddress, shouldUseLedger); @@ -787,15 +790,20 @@ export class Blockchain { shouldPollUserAddress: boolean = false, useLedgerProvider: boolean = false, ): Promise { + this._dispatcher.updateBlockchainIsLoaded(false); + this._dispatcher.updateUserWeiBalance(undefined); this.networkId = networkId; - this._dispatcher.updateNetworkId(networkId); const injectedWeb3 = Blockchain._getInjectedWeb3(); - const provider = await Blockchain._getProviderAsync(injectedWeb3, networkId, useLedgerProvider); - // if (!_.isUndefined(this._contractWrappers)) { - // this._contractWrappers.setProvider(provider, networkId); - // } else { - // } - this._contractWrappers = new ContractWrappers(provider, { networkId }); + const [provider, ledgerSubproviderIfExists] = await Blockchain._getProviderAsync( + injectedWeb3, + networkId, + useLedgerProvider, + ); + if (!_.isUndefined(this._contractWrappers)) { + this._contractWrappers.setProvider(provider, networkId); + } else { + this._contractWrappers = new ContractWrappers(provider, { networkId }); + } if (!_.isUndefined(this._blockchainWatcher)) { this._blockchainWatcher.destroy(); } @@ -806,9 +814,10 @@ export class Blockchain { this.networkId, shouldPollUserAddress, ); - if (useLedgerProvider) { + if (useLedgerProvider && !_.isUndefined(ledgerSubproviderIfExists)) { // TODO: why? delete this._userAddressIfExists; + this._ledgerSubprovider = ledgerSubproviderIfExists; this._dispatcher.updateUserAddress(undefined); this._dispatcher.updateProviderType(ProviderType.Ledger); } else { @@ -816,11 +825,13 @@ export class Blockchain { const userAddresses = await this._web3Wrapper.getAvailableAddressesAsync(); this._userAddressIfExists = userAddresses[0]; this._dispatcher.updateUserAddress(this._userAddressIfExists); - this._updateProviderName(injectedWeb3); + if (!_.isUndefined(injectedWeb3)) { + this._dispatcher.updateProviderType(ProviderType.Injected); + } } - // TOOD: should not call this in ledger case? await this.fetchTokenInformationAsync(); await this._blockchainWatcher.startEmittingUserBalanceStateAsync(); + this._dispatcher.updateNetworkId(networkId); await this._rehydrateStoreWithContractEventsAsync(); } private _updateProviderName(injectedWeb3: Web3): void { diff --git a/packages/website/ts/blockchain_watcher.ts b/packages/website/ts/blockchain_watcher.ts index 5d029d4f1..4112c8a15 100644 --- a/packages/website/ts/blockchain_watcher.ts +++ b/packages/website/ts/blockchain_watcher.ts @@ -35,16 +35,6 @@ export class BlockchainWatcher { public updatePrevUserAddress(userAddress: string): void { this._prevUserAddressIfExists = userAddress; } - // public async startEmittingInjectedProviderNetworkIdAsync(injectedProvider: any): Promise { - // if (this._isWatchingNetworkId) { - // return; // we are already watching the network id - // } - // const observable = injectedProvider.publicConfigStore; - // if (observable && observable.subscribe) { - // observable.subscribe(this._handleInjectedProviderUpdate.bind(this)); - // this._isWatchingNetworkId = true; - // } - // } public async startEmittingUserBalanceStateAsync(): Promise { if (!_.isUndefined(this._watchBalanceIntervalId)) { return; // we are already emitting the state @@ -60,15 +50,6 @@ export class BlockchainWatcher { }, ); } - // private _handleInjectedProviderUpdate(update: InjectedProviderUpdate): void { - // const updatedNetworkId = _.parseInt(update.networkVersion); - // if (this._prevNetworkId === updatedNetworkId) { - // return; - // } - // this._prevNetworkId = updatedNetworkId; - // this._dispatcher.updateNetworkId(updatedNetworkId); - // this._updateBalanceAsync(); - // } private async _updateBalanceAsync(): Promise { let prevNodeVersion: string; // Check for node version changes diff --git a/packages/website/ts/components/portal/portal.tsx b/packages/website/ts/components/portal/portal.tsx index 58acb435e..bb88e3824 100644 --- a/packages/website/ts/components/portal/portal.tsx +++ b/packages/website/ts/components/portal/portal.tsx @@ -182,9 +182,10 @@ export class Portal extends React.Component { prevPathname: nextProps.location.pathname, }); } + + // If the address changed, but the network did not, we can just refetch the currently tracked tokens. if ( - nextProps.userAddress !== this.props.userAddress || - nextProps.networkId !== this.props.networkId || + (nextProps.userAddress !== this.props.userAddress && nextProps.networkId === this.props.networkId) || nextProps.lastForceTokenStateRefetch !== this.props.lastForceTokenStateRefetch ) { const trackedTokenAddresses = _.keys(this.state.trackedTokenStateByAddress); @@ -200,7 +201,7 @@ export class Portal extends React.Component { const newTokenAddresses = _.map(newTokens, token => token.address); // Add placeholder entry for this token to the state, since fetching the // balance/allowance is asynchronous - const trackedTokenStateByAddress = this.state.trackedTokenStateByAddress; + const trackedTokenStateByAddress = { ...this.state.trackedTokenStateByAddress }; for (const tokenAddress of newTokenAddresses) { trackedTokenStateByAddress[tokenAddress] = { balance: new BigNumber(0), -- cgit From e27780aee9de63790da0ebd610761458f42c9259 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 26 Jun 2018 11:59:23 -0700 Subject: Fix subtle issues with ledger config dialog --- packages/website/ts/blockchain.ts | 2 +- .../ts/components/dialogs/ledger_config_dialog.tsx | 2 ++ packages/website/ts/components/portal/portal.tsx | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index d55cb6771..d2040abb5 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -828,8 +828,8 @@ export class Blockchain { if (!_.isUndefined(injectedWeb3)) { this._dispatcher.updateProviderType(ProviderType.Injected); } + await this.fetchTokenInformationAsync(); } - await this.fetchTokenInformationAsync(); await this._blockchainWatcher.startEmittingUserBalanceStateAsync(); this._dispatcher.updateNetworkId(networkId); await this._rehydrateStoreWithContractEventsAsync(); diff --git a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx index c9727b553..69144bdd3 100644 --- a/packages/website/ts/components/dialogs/ledger_config_dialog.tsx +++ b/packages/website/ts/components/dialogs/ledger_config_dialog.tsx @@ -283,6 +283,8 @@ export class LedgerConfigDialog extends React.Component { networkId={this.props.networkId} /> - {this.props.blockchainIsLoaded && ( - - )} + + + Date: Tue, 26 Jun 2018 12:09:20 -0700 Subject: Get things working with legacy portal --- .../ts/components/legacy_portal/legacy_portal.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/website/ts/components/legacy_portal/legacy_portal.tsx b/packages/website/ts/components/legacy_portal/legacy_portal.tsx index b4a174a03..a52c79e26 100644 --- a/packages/website/ts/components/legacy_portal/legacy_portal.tsx +++ b/packages/website/ts/components/legacy_portal/legacy_portal.tsx @@ -249,16 +249,14 @@ export class LegacyPortal extends React.Component - {this.props.blockchainIsLoaded && ( - - )} +