diff --git a/package.json b/package.json index 7c54c1a18c..f25438224b 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "wsrun": "^2.2.0" }, "resolutions": { - "ethereumjs-tx": "0xProject/ethereumjs-tx#fake-tx-include-signature-by-default" + "ethereumjs-tx": "0xProject/ethereumjs-tx#fake-tx-include-signature-by-default", + "ethers": "0xproject/ethers.js#eip-838-reasons" } } diff --git a/packages/base-contract/src/index.ts b/packages/base-contract/src/index.ts index d9e28f9f2c..64783b469d 100644 --- a/packages/base-contract/src/index.ts +++ b/packages/base-contract/src/index.ts @@ -79,8 +79,7 @@ export class BaseContract { // Awaiting https://github.com/Microsoft/TypeScript/pull/13288 to be merged } as any; if (_.isUndefined(txDataWithDefaults.gas) && !_.isUndefined(estimateGasAsync)) { - const estimatedGas = await estimateGasAsync(txData); - txDataWithDefaults.gas = estimatedGas; + txDataWithDefaults.gas = await estimateGasAsync(txData); } return txDataWithDefaults; } diff --git a/packages/contracts/README.md b/packages/contracts/README.md index 23551b69d8..9c829c7535 100644 --- a/packages/contracts/README.md +++ b/packages/contracts/README.md @@ -63,3 +63,12 @@ yarn lint ```bash yarn test ``` + +### Run Tests Against Geth + +Follow the instructions in the README for the devnet package to start the +devnet. + +```bash +TEST_PROVIDER=geth yarn test +``` diff --git a/packages/contracts/src/utils/assertions.ts b/packages/contracts/src/utils/assertions.ts new file mode 100644 index 0000000000..615e648f38 --- /dev/null +++ b/packages/contracts/src/utils/assertions.ts @@ -0,0 +1,63 @@ +import * as chai from 'chai'; +import * as _ from 'lodash'; + +import { constants } from './constants'; + +const expect = chai.expect; + +function _expectEitherErrorAsync(p: Promise, error1: string, error2: string): PromiseLike { + return expect(p) + .to.be.rejected() + .then(e => { + expect(e).to.satisfy( + (err: Error) => _.includes(err.message, error1) || _.includes(err.message, error2), + `expected promise to reject with error message that includes "${error1}" or "${error2}", but got: ` + + `"${e.message}"\n`, + ); + }); +} + +/** + * Rejects if the given Promise does not reject with an error indicating + * insufficient funds. + * @param p the Promise which is expected to reject + * @returns a new Promise which will reject if the conditions are not met and + * otherwise resolve with no value. + */ +export function expectInsufficientFundsAsync(p: Promise): PromiseLike { + return _expectEitherErrorAsync(p, 'insufficient funds', "sender doesn't have enough funds"); +} + +/** + * Rejects if the given Promise does not reject with a "revert" error or the + * given otherError. + * @param p the Promise which is expected to reject + * @param otherError the other error which is accepted as a valid reject error. + * @returns a new Promise which will reject if the conditions are not met and + * otherwise resolve with no value. + */ +export function expectRevertOrOtherErrorAsync(p: Promise, otherError: string): PromiseLike { + return _expectEitherErrorAsync(p, constants.REVERT, otherError); +} + +/** + * Rejects if the given Promise does not reject with a "revert" or "always + * failing transaction" error. + * @param p the Promise which is expected to reject + * @returns a new Promise which will reject if the conditions are not met and + * otherwise resolve with no value. + */ +export function expectRevertOrAlwaysFailingTransactionAsync(p: Promise): PromiseLike { + return expectRevertOrOtherErrorAsync(p, 'always failing transaction'); +} + +/** + * Rejects if the given Promise does not reject with a "revert" or "Contract + * call failed" error. + * @param p the Promise which is expected to reject + * @returns a new Promise which will reject if the conditions are not met and + * otherwise resolve with no value. + */ +export function expectRevertOrContractCallFailedAsync(p: Promise): PromiseLike { + return expectRevertOrOtherErrorAsync(p, 'Contract call failed'); +} diff --git a/packages/contracts/src/utils/constants.ts b/packages/contracts/src/utils/constants.ts index 9b0b92545b..fa2a4af3c4 100644 --- a/packages/contracts/src/utils/constants.ts +++ b/packages/contracts/src/utils/constants.ts @@ -19,6 +19,13 @@ const TESTRPC_PRIVATE_KEYS_STRINGS = [ export const constants = { INVALID_OPCODE: 'invalid opcode', REVERT: 'revert', + LIB_BYTES_GT_ZERO_LENGTH_REQUIRED: 'Length must be greater than 0.', + LIB_BYTES_GTE_4_LENGTH_REQUIRED: 'Length must be greater than or equal to 4.', + LIB_BYTES_GTE_20_LENGTH_REQUIRED: 'Length must be greater than or equal to 20.', + LIB_BYTES_GTE_32_LENGTH_REQUIRED: 'Length must be greater than or equal to 32.', + LIB_BYTES_INDEX_OUT_OF_BOUNDS: 'Specified array index is out of bounds.', + ERC20_INSUFFICIENT_BALANCE: 'Insufficient balance to complete transfer.', + ERC20_INSUFFICIENT_ALLOWANCE: 'Insufficient allowance to complete transfer.', TESTRPC_NETWORK_ID: 50, AWAIT_TRANSACTION_MINED_MS: 100, MAX_ETHERTOKEN_WITHDRAW_GAS: 43000, diff --git a/packages/contracts/src/utils/exchange_wrapper.ts b/packages/contracts/src/utils/exchange_wrapper.ts index dd278e77cd..24c3ba4be5 100644 --- a/packages/contracts/src/utils/exchange_wrapper.ts +++ b/packages/contracts/src/utils/exchange_wrapper.ts @@ -60,14 +60,14 @@ export class ExchangeWrapper { public async fillOrderNoThrowAsync( signedOrder: SignedOrder, from: string, - opts: { takerAssetFillAmount?: BigNumber } = {}, + opts: { takerAssetFillAmount?: BigNumber; gas?: number } = {}, ): Promise { const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); const txHash = await this._exchange.fillOrderNoThrow.sendTransactionAsync( params.order, params.takerAssetFillAmount, params.signature, - { from }, + { from, gas: opts.gas }, ); const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); return tx; @@ -105,14 +105,14 @@ export class ExchangeWrapper { public async batchFillOrdersNoThrowAsync( orders: SignedOrder[], from: string, - opts: { takerAssetFillAmounts?: BigNumber[] } = {}, + opts: { takerAssetFillAmounts?: BigNumber[]; gas?: number } = {}, ): Promise { const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts); const txHash = await this._exchange.batchFillOrdersNoThrow.sendTransactionAsync( params.orders, params.takerAssetFillAmounts, params.signatures, - { from }, + { from, gas: opts.gas }, ); const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); return tx; @@ -135,14 +135,14 @@ export class ExchangeWrapper { public async marketSellOrdersNoThrowAsync( orders: SignedOrder[], from: string, - opts: { takerAssetFillAmount: BigNumber }, + opts: { takerAssetFillAmount: BigNumber; gas?: number }, ): Promise { const params = formatters.createMarketSellOrders(orders, opts.takerAssetFillAmount); const txHash = await this._exchange.marketSellOrdersNoThrow.sendTransactionAsync( params.orders, params.takerAssetFillAmount, params.signatures, - { from }, + { from, gas: opts.gas }, ); const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); return tx; diff --git a/packages/contracts/src/utils/increase_time.ts b/packages/contracts/src/utils/increase_time.ts new file mode 100644 index 0000000000..4565d8dbc5 --- /dev/null +++ b/packages/contracts/src/utils/increase_time.ts @@ -0,0 +1,31 @@ +import * as _ from 'lodash'; + +import { constants } from './constants'; +import { web3Wrapper } from './web3_wrapper'; + +let firstAccount: string | undefined; + +/** + * Increases time by the given number of seconds and then mines a block so that + * the current block timestamp has the offset applied. + * @param seconds the number of seconds by which to incrase the time offset. + * @returns a new Promise which will resolve with the new total time offset or + * reject if the time could not be increased. + */ +export async function increaseTimeAndMineBlockAsync(seconds: number): Promise { + if (_.isUndefined(firstAccount)) { + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + firstAccount = accounts[0]; + } + + const offset = await web3Wrapper.increaseTimeAsync(seconds); + // Note: we need to send a transaction after increasing time so + // that a block is actually mined. The contract looks at the + // last mined block for the timestamp. + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ from: firstAccount, to: firstAccount, value: 0 }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + return offset; +} diff --git a/packages/contracts/src/utils/web3_wrapper.ts b/packages/contracts/src/utils/web3_wrapper.ts index 1049ab9674..63ce2c8cce 100644 --- a/packages/contracts/src/utils/web3_wrapper.ts +++ b/packages/contracts/src/utils/web3_wrapper.ts @@ -5,15 +5,50 @@ import { Provider } from 'ethereum-types'; import { coverage } from './coverage'; -export const txDefaults = { +enum ProviderType { + Ganache = 'ganache', + Geth = 'geth', +} + +let testProvider: ProviderType; +switch (process.env.TEST_PROVIDER) { + case undefined: + testProvider = ProviderType.Ganache; + break; + case 'ganache': + testProvider = ProviderType.Ganache; + break; + case 'geth': + testProvider = ProviderType.Geth; + break; + default: + throw new Error(`Unknown TEST_PROVIDER: ${process.env.TEST_PROVIDER}`); +} + +const ganacheTxDefaults = { from: devConstants.TESTRPC_FIRST_ADDRESS, gas: devConstants.GAS_LIMIT, }; -const providerConfigs = { shouldUseInProcessGanache: true }; +const gethTxDefaults = { + from: devConstants.TESTRPC_FIRST_ADDRESS, +}; +export const txDefaults = testProvider === ProviderType.Ganache ? ganacheTxDefaults : gethTxDefaults; + +const gethConfigs = { + shouldUseInProcessGanache: false, + rpcUrl: 'http://localhost:8501', + shouldUseFakeGasEstimate: false, +}; +const ganacheConfigs = { + shouldUseInProcessGanache: true, +}; +const providerConfigs = testProvider === ProviderType.Ganache ? ganacheConfigs : gethConfigs; + export const provider = web3Factory.getRpcProvider(providerConfigs); const isCoverageEnabled = env.parseBoolean(EnvVars.SolidityCoverage); if (isCoverageEnabled) { const coverageSubprovider = coverage.getCoverageSubproviderSingleton(); prependSubprovider(provider, coverageSubprovider); } + export const web3Wrapper = new Web3Wrapper(provider); diff --git a/packages/contracts/test/asset_proxy/authorizable.ts b/packages/contracts/test/asset_proxy/authorizable.ts index e8274acb1f..945208d820 100644 --- a/packages/contracts/test/asset_proxy/authorizable.ts +++ b/packages/contracts/test/asset_proxy/authorizable.ts @@ -6,6 +6,7 @@ import * as Web3 from 'web3'; import { MixinAuthorizableContract } from '../../src/contract_wrappers/generated/mixin_authorizable'; import { artifacts } from '../../src/utils/artifacts'; +import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; @@ -44,9 +45,9 @@ describe('Authorizable', () => { }); describe('addAuthorizedAddress', () => { it('should throw if not called by owner', async () => { - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should allow owner to add an authorized address', async () => { await web3Wrapper.awaitTransactionSuccessAsync( @@ -61,9 +62,9 @@ describe('Authorizable', () => { await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), constants.AWAIT_TRANSACTION_MINED_MS, ); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); @@ -73,11 +74,11 @@ describe('Authorizable', () => { await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), constants.AWAIT_TRANSACTION_MINED_MS, ); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { from: notOwner, }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should allow owner to remove an authorized address', async () => { @@ -96,11 +97,11 @@ describe('Authorizable', () => { }); it('should throw if owner attempts to remove an address that is not authorized', async () => { - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { from: owner, }), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts index faab397597..dac790f955 100644 --- a/packages/contracts/test/asset_proxy/proxies.ts +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -10,6 +10,7 @@ import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/d 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 { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; @@ -144,7 +145,7 @@ describe('Asset Transfer Proxies', () => { constants.AWAIT_TRANSACTION_MINED_MS, ); // Perform a transfer; expect this to fail. - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( erc20Proxy.transferFrom.sendTransactionAsync( encodedProxyMetadata, makerAddress, @@ -152,7 +153,7 @@ describe('Asset Transfer Proxies', () => { transferAmount, { from: notAuthorized }, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw if requesting address is not authorized', async () => { @@ -160,7 +161,7 @@ describe('Asset Transfer Proxies', () => { const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrxToken.address); // Perform a transfer from makerAddress to takerAddress const amount = new BigNumber(10); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( erc20Proxy.transferFrom.sendTransactionAsync( encodedProxyMetadata, makerAddress, @@ -170,7 +171,7 @@ describe('Asset Transfer Proxies', () => { from: notAuthorized, }, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); @@ -217,7 +218,7 @@ describe('Asset Transfer Proxies', () => { const toAddresses = _.times(numTransfers, () => takerAddress); const amounts = _.times(numTransfers, () => amount); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( erc20Proxy.batchTransferFrom.sendTransactionAsync( assetMetadata, fromAddresses, @@ -225,7 +226,7 @@ describe('Asset Transfer Proxies', () => { amounts, { from: notAuthorized }, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); @@ -276,7 +277,7 @@ describe('Asset Transfer Proxies', () => { // Perform a transfer from makerAddress to takerAddress const erc20Balances = await erc20Wrapper.getBalancesAsync(); const amount = new BigNumber(0); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( erc721Proxy.transferFrom.sendTransactionAsync( encodedProxyMetadata, makerAddress, @@ -284,7 +285,7 @@ describe('Asset Transfer Proxies', () => { amount, { from: exchangeAddress }, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw if transferring > 1 amount of a token', async () => { @@ -299,7 +300,7 @@ describe('Asset Transfer Proxies', () => { // Perform a transfer from makerAddress to takerAddress const erc20Balances = await erc20Wrapper.getBalancesAsync(); const amount = new BigNumber(500); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( erc721Proxy.transferFrom.sendTransactionAsync( encodedProxyMetadata, makerAddress, @@ -307,7 +308,7 @@ describe('Asset Transfer Proxies', () => { amount, { from: exchangeAddress }, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw if allowances are too low', async () => { @@ -325,7 +326,7 @@ describe('Asset Transfer Proxies', () => { ); // Perform a transfer; expect this to fail. const amount = new BigNumber(1); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( erc20Proxy.transferFrom.sendTransactionAsync( encodedProxyMetadata, makerAddress, @@ -335,7 +336,7 @@ describe('Asset Transfer Proxies', () => { from: notAuthorized, }, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw if requesting address is not authorized', async () => { @@ -346,7 +347,7 @@ describe('Asset Transfer Proxies', () => { ); // Perform a transfer from makerAddress to takerAddress const amount = new BigNumber(1); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( erc721Proxy.transferFrom.sendTransactionAsync( encodedProxyMetadata, makerAddress, @@ -354,7 +355,7 @@ describe('Asset Transfer Proxies', () => { amount, { from: notAuthorized }, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); @@ -404,7 +405,7 @@ describe('Asset Transfer Proxies', () => { const toAddresses = _.times(numTransfers, () => takerAddress); const amounts = _.times(numTransfers, () => new BigNumber(1)); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( erc721Proxy.batchTransferFrom.sendTransactionAsync( assetMetadata, fromAddresses, @@ -412,7 +413,7 @@ describe('Asset Transfer Proxies', () => { amounts, { from: notAuthorized }, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); diff --git a/packages/contracts/test/asset_proxy_owner.ts b/packages/contracts/test/asset_proxy_owner.ts index 4c16b5cffd..cdf63645ff 100644 --- a/packages/contracts/test/asset_proxy_owner.ts +++ b/packages/contracts/test/asset_proxy_owner.ts @@ -15,8 +15,13 @@ import { } from '../src/contract_wrappers/generated/asset_proxy_owner'; import { MixinAuthorizableContract } from '../src/contract_wrappers/generated/mixin_authorizable'; import { artifacts } from '../src/utils/artifacts'; +import { + expectRevertOrAlwaysFailingTransactionAsync, + expectRevertOrContractCallFailedAsync, +} from '../src/utils/assertions'; import { chaiSetup } from '../src/utils/chai_setup'; import { constants } from '../src/utils/constants'; +import { increaseTimeAndMineBlockAsync } from '../src/utils/increase_time'; import { MultiSigWrapper } from '../src/utils/multi_sig_wrapper'; import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper'; @@ -101,7 +106,7 @@ describe('AssetProxyOwner', () => { }); it('should throw if a null address is included in assetProxyContracts', async () => { const assetProxyContractAddresses = [erc20Proxy.address, constants.NULL_ADDRESS]; - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( AssetProxyOwnerContract.deployFrom0xArtifactAsync( artifacts.AssetProxyOwner, provider, @@ -111,7 +116,7 @@ describe('AssetProxyOwner', () => { REQUIRED_APPROVALS, SECONDS_TIME_LOCKED, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); @@ -120,9 +125,9 @@ describe('AssetProxyOwner', () => { const notRemoveAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData( owners[0], ); - return expect( + return expectRevertOrContractCallFailedAsync( multiSig.isFunctionRemoveAuthorizedAddress.callAsync(notRemoveAuthorizedAddressData), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should return true if data is for removeAuthorizedAddress', async () => { @@ -139,9 +144,9 @@ describe('AssetProxyOwner', () => { describe('registerAssetProxy', () => { it('should throw if not called by multisig', async () => { const isRegistered = true; - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( multiSig.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, isRegistered, { from: owners[0] }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should register an address if called by multisig after timelock', async () => { @@ -156,11 +161,12 @@ describe('AssetProxyOwner', () => { registerAssetProxyData, owners[0], ); + const log = submitTxRes.logs[0] as LogWithDecodedArgs; const txId = log.args.transactionId; await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); - await web3Wrapper.increaseTimeAsync(SECONDS_TIME_LOCKED.toNumber()); + await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber()); const executeTxRes = await multiSigWrapper.executeTransactionAsync(txId, owners[0]); const registerLog = executeTxRes.logs[0] as LogWithDecodedArgs; @@ -187,7 +193,7 @@ describe('AssetProxyOwner', () => { const txId = log.args.transactionId; await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); - await web3Wrapper.increaseTimeAsync(SECONDS_TIME_LOCKED.toNumber()); + await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber()); const executeTxRes = await multiSigWrapper.executeTransactionAsync(txId, owners[0]); const failureLog = executeTxRes.logs[0] as LogWithDecodedArgs; @@ -239,7 +245,7 @@ describe('AssetProxyOwner', () => { await multiSigWrapper.confirmTransactionAsync(erc20AddAuthorizedAddressTxId, owners[1]); await multiSigWrapper.confirmTransactionAsync(erc721AddAuthorizedAddressTxId, owners[1]); - await web3Wrapper.increaseTimeAsync(SECONDS_TIME_LOCKED.toNumber()); + await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber()); await multiSigWrapper.executeTransactionAsync(registerAssetProxyTxId, owners[0]); await multiSigWrapper.executeTransactionAsync(erc20AddAuthorizedAddressTxId, owners[0]); await multiSigWrapper.executeTransactionAsync(erc721AddAuthorizedAddressTxId, owners[0]); @@ -257,9 +263,9 @@ describe('AssetProxyOwner', () => { const log = res.logs[0] as LogWithDecodedArgs; const txId = log.args.transactionId; - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw if tx destination is not registered', async () => { @@ -276,9 +282,9 @@ describe('AssetProxyOwner', () => { await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw if tx data is not for removeAuthorizedAddress', async () => { @@ -296,9 +302,9 @@ describe('AssetProxyOwner', () => { await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should execute removeAuthorizedAddress for registered address if fully confirmed', async () => { @@ -349,9 +355,9 @@ describe('AssetProxyOwner', () => { const isExecuted = tx[3]; expect(isExecuted).to.equal(true); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); }); diff --git a/packages/contracts/test/ether_token.ts b/packages/contracts/test/ether_token.ts index 4e52b658f2..e9331b16ed 100644 --- a/packages/contracts/test/ether_token.ts +++ b/packages/contracts/test/ether_token.ts @@ -6,6 +6,7 @@ import 'make-promises-safe'; import { WETH9Contract } from '../src/contract_wrappers/generated/weth9'; import { artifacts } from '../src/utils/artifacts'; +import { expectInsufficientFundsAsync, expectRevertOrAlwaysFailingTransactionAsync } from '../src/utils/assertions'; import { chaiSetup } from '../src/utils/chai_setup'; import { constants } from '../src/utils/constants'; import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper'; @@ -45,9 +46,7 @@ describe('EtherToken', () => { const initEthBalance = await web3Wrapper.getBalanceInWeiAsync(account); const ethToDeposit = initEthBalance.plus(1); - return expect(etherToken.deposit.sendTransactionAsync({ value: ethToDeposit })).to.be.rejectedWith( - "ender doesn't have enough funds to send tx.", - ); + return expectInsufficientFundsAsync(etherToken.deposit.sendTransactionAsync({ value: ethToDeposit })); }); it('should convert deposited Ether to wrapped Ether tokens', async () => { @@ -76,8 +75,8 @@ describe('EtherToken', () => { const initEthTokenBalance = await etherToken.balanceOf.callAsync(account); const ethTokensToWithdraw = initEthTokenBalance.plus(1); - return expect(etherToken.withdraw.sendTransactionAsync(ethTokensToWithdraw)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + etherToken.withdraw.sendTransactionAsync(ethTokensToWithdraw), ); }); diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index 91ead93f0a..17d416fd62 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -18,6 +18,7 @@ import { FillContractEventArgs, } from '../../src/contract_wrappers/generated/exchange'; import { artifacts } from '../../src/utils/artifacts'; +import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; @@ -415,8 +416,8 @@ describe('Exchange core', () => { makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), }); - return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), ); }); @@ -432,8 +433,8 @@ describe('Exchange core', () => { const invalidSigBuff = Buffer.concat([v, invalidR, invalidS, signatureType]); const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`; signedOrder.signature = invalidSigHex; - return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), ); }); @@ -442,8 +443,8 @@ describe('Exchange core', () => { makerAssetAmount: new BigNumber(0), }); - return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), ); }); @@ -452,19 +453,19 @@ describe('Exchange core', () => { takerAssetAmount: new BigNumber(0), }); - return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), ); }); it('should throw if takerAssetFillAmount is 0', async () => { signedOrder = orderFactory.newSignedOrder(); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount: new BigNumber(0), }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw if maker erc20Balances are too low to fill order', async () => { @@ -472,8 +473,8 @@ describe('Exchange core', () => { makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18), }); - return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), ); }); @@ -481,9 +482,8 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18), }); - - return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), ); }); @@ -494,11 +494,8 @@ describe('Exchange core', () => { }), constants.AWAIT_TRANSACTION_MINED_MS, ); - // HACK: `rejectWith` returns a "promise-like" type, but not an actual "Promise", so TSLint - // complains, even though we do need to `await` it. So we disable the TSLint error below. - // tslint:disable-next-line:await-promise - await expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), ); }); @@ -509,11 +506,8 @@ describe('Exchange core', () => { }), constants.AWAIT_TRANSACTION_MINED_MS, ); - // HACK: `rejectWith` returns a "promise-like" type, but not an actual "Promise", so TSLint - // complains, even though we do need to `await` it. So we disable the TSLint error below. - // tslint:disable-next-line:await-promise - await expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), ); }); @@ -521,16 +515,16 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)), }); - return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), ); }); it('should throw if no value is filled', async () => { signedOrder = orderFactory.newSignedOrder(); await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); - return expect(exchangeWrapper.fillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.fillOrderAsync(signedOrder, takerAddress), ); }); }); @@ -542,8 +536,8 @@ describe('Exchange core', () => { }); it('should throw if not sent by maker', async () => { - return expect(exchangeWrapper.cancelOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.cancelOrderAsync(signedOrder, takerAddress), ); }); @@ -552,8 +546,8 @@ describe('Exchange core', () => { makerAssetAmount: new BigNumber(0), }); - return expect(exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), ); }); @@ -562,18 +556,18 @@ describe('Exchange core', () => { takerAssetAmount: new BigNumber(0), }); - return expect(exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), ); }); it('should be able to cancel a full order', async () => { await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount: signedOrder.takerAssetAmount.div(2), }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should log 1 event with correct arguments', async () => { @@ -593,8 +587,8 @@ describe('Exchange core', () => { it('should throw if already cancelled', async () => { await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress); - return expect(exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), ); }); @@ -602,8 +596,8 @@ describe('Exchange core', () => { signedOrder = orderFactory.newSignedOrder({ expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)), }); - return expect(exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress), ); }); @@ -619,11 +613,11 @@ describe('Exchange core', () => { }); const fillTakerAssetAmount2 = new BigNumber(1); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount: fillTakerAssetAmount2, }), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); @@ -632,16 +626,16 @@ describe('Exchange core', () => { const makerEpoch = new BigNumber(1); await exchangeWrapper.cancelOrdersUpToAsync(makerEpoch, makerAddress); const lesserMakerEpoch = new BigNumber(0); - return expect(exchangeWrapper.cancelOrdersUpToAsync(lesserMakerEpoch, makerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.cancelOrdersUpToAsync(lesserMakerEpoch, makerAddress), ); }); it('should fail to set makerEpoch equal to existing makerEpoch', async () => { const makerEpoch = new BigNumber(1); await exchangeWrapper.cancelOrdersUpToAsync(makerEpoch, makerAddress); - return expect(exchangeWrapper.cancelOrdersUpToAsync(makerEpoch, makerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.cancelOrdersUpToAsync(makerEpoch, makerAddress), ); }); @@ -675,7 +669,12 @@ describe('Exchange core', () => { salt: new BigNumber(3), }), ]; - await exchangeWrapper.batchFillOrdersNoThrowAsync(signedOrders, takerAddress); + await exchangeWrapper.batchFillOrdersNoThrowAsync(signedOrders, takerAddress, { + // HACK(albrow): We need to hardcode the gas estimate here because + // the Geth gas estimator doesn't work with the way we use + // delegatecall and swallow errors. + gas: 490000, + }); const newBalances = await erc20Wrapper.getBalancesAsync(); const fillMakerAssetAmount = signedOrders[2].makerAssetAmount.add(signedOrders[3].makerAssetAmount); @@ -749,9 +748,9 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw when taker does not own the token with id takerAssetId', async () => { @@ -771,9 +770,9 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.not.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw when makerAssetAmount is greater than 1', async () => { @@ -793,9 +792,9 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw when takerAssetAmount is greater than 1', async () => { @@ -815,9 +814,9 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw on partial fill', async () => { @@ -837,9 +836,9 @@ describe('Exchange core', () => { expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should successfully fill order when makerAsset is ERC721 and takerAsset is ERC20', async () => { diff --git a/packages/contracts/test/exchange/dispatcher.ts b/packages/contracts/test/exchange/dispatcher.ts index 8bc66e3cfc..1e956e72e8 100644 --- a/packages/contracts/test/exchange/dispatcher.ts +++ b/packages/contracts/test/exchange/dispatcher.ts @@ -10,6 +10,7 @@ import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c2 import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy'; import { TestAssetProxyDispatcherContract } from '../../src/contract_wrappers/generated/test_asset_proxy_dispatcher'; import { artifacts } from '../../src/utils/artifacts'; +import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; @@ -177,14 +178,14 @@ describe('AssetProxyDispatcher', () => { const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); expect(proxyAddress).to.be.equal(erc20Proxy.address); // The following transaction will throw because the currentAddress is no longer constants.NULL_ADDRESS - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( AssetProxyId.ERC20, erc20Proxy.address, constants.NULL_ADDRESS, { from: owner }, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should be able to reset proxy address to NULL', async () => { @@ -218,26 +219,26 @@ describe('AssetProxyDispatcher', () => { it('should throw if requesting address is not owner', async () => { const prevProxyAddress = constants.NULL_ADDRESS; - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( AssetProxyId.ERC20, erc20Proxy.address, prevProxyAddress, { from: notOwner }, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw if attempting to register a proxy to the incorrect id', async () => { const prevProxyAddress = constants.NULL_ADDRESS; - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( AssetProxyId.ERC721, erc20Proxy.address, prevProxyAddress, { from: owner }, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); @@ -307,7 +308,7 @@ describe('AssetProxyDispatcher', () => { // Perform a transfer from makerAddress to takerAddress const erc20Balances = await erc20Wrapper.getBalancesAsync(); const amount = new BigNumber(10); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync( encodedProxyMetadata, makerAddress, @@ -315,7 +316,7 @@ describe('AssetProxyDispatcher', () => { amount, { from: owner }, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); }); diff --git a/packages/contracts/test/exchange/match_orders.ts b/packages/contracts/test/exchange/match_orders.ts index 24ee794bc8..b1685dd4ae 100644 --- a/packages/contracts/test/exchange/match_orders.ts +++ b/packages/contracts/test/exchange/match_orders.ts @@ -18,6 +18,7 @@ import { FillContractEventArgs, } from '../../src/contract_wrappers/generated/exchange'; import { artifacts } from '../../src/utils/artifacts'; +import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; @@ -639,9 +640,9 @@ describe('matchOrders', () => { // Cancel left order await exchangeWrapper.cancelOrderAsync(signedOrderLeft, signedOrderLeft.makerAddress); // Match orders - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('Should throw if right order is not fillable', async () => { @@ -665,9 +666,9 @@ describe('matchOrders', () => { // Cancel right order await exchangeWrapper.cancelOrderAsync(signedOrderRight, signedOrderRight.makerAddress); // Match orders - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, takerAddress), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw if there is not a positive spread', async () => { @@ -689,7 +690,7 @@ describe('matchOrders', () => { feeRecipientAddress: feeRecipientAddressRight, }); // Match orders - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, @@ -697,7 +698,7 @@ describe('matchOrders', () => { erc20BalancesByOwner, erc721TokenIdsByOwner, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw if the left maker asset is not equal to the right taker asset ', async () => { @@ -719,7 +720,7 @@ describe('matchOrders', () => { feeRecipientAddress: feeRecipientAddressRight, }); // Match orders - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, @@ -727,7 +728,7 @@ describe('matchOrders', () => { erc20BalancesByOwner, erc721TokenIdsByOwner, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw if the right maker asset is not equal to the left taker asset', async () => { @@ -749,7 +750,7 @@ describe('matchOrders', () => { feeRecipientAddress: feeRecipientAddressRight, }); // Match orders - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( matchOrderTester.matchOrdersAndVerifyBalancesAsync( signedOrderLeft, signedOrderRight, @@ -757,7 +758,7 @@ describe('matchOrders', () => { erc20BalancesByOwner, erc721TokenIdsByOwner, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should transfer correct amounts when left order maker asset is an ERC721 token', async () => { diff --git a/packages/contracts/test/exchange/transactions.ts b/packages/contracts/test/exchange/transactions.ts index f31053ad3b..e64cb68f5a 100644 --- a/packages/contracts/test/exchange/transactions.ts +++ b/packages/contracts/test/exchange/transactions.ts @@ -11,6 +11,7 @@ import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c2 import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange'; import { WhitelistContract } from '../../src/contract_wrappers/generated/whitelist'; import { artifacts } from '../../src/utils/artifacts'; +import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; @@ -126,8 +127,8 @@ describe('Exchange transactions', () => { }); it('should throw if not called by specified sender', async () => { - return expect(exchangeWrapper.executeTransactionAsync(signedTx, takerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.executeTransactionAsync(signedTx, takerAddress), ); }); @@ -168,8 +169,8 @@ describe('Exchange transactions', () => { it('should throw if the a 0x transaction with the same transactionHash has already been executed', async () => { await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress); - return expect(exchangeWrapper.executeTransactionAsync(signedTx, senderAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.executeTransactionAsync(signedTx, senderAddress), ); }); @@ -187,15 +188,15 @@ describe('Exchange transactions', () => { }); it('should throw if not called by specified sender', async () => { - return expect(exchangeWrapper.executeTransactionAsync(signedTx, makerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.executeTransactionAsync(signedTx, makerAddress), ); }); it('should cancel the order when signed by maker and called by sender', async () => { await exchangeWrapper.executeTransactionAsync(signedTx, senderAddress); - return expect(exchangeWrapper.fillOrderAsync(signedOrder, senderAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.fillOrderAsync(signedOrder, senderAddress), ); }); }); @@ -244,7 +245,7 @@ describe('Exchange transactions', () => { orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); const takerAssetFillAmount = signedOrder.takerAssetAmount; const salt = generatePseudoRandomSalt(); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( whitelist.fillOrderIfWhitelisted.sendTransactionAsync( orderWithoutExchangeAddress, takerAssetFillAmount, @@ -252,7 +253,7 @@ describe('Exchange transactions', () => { signedOrder.signature, { from: takerAddress }, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should revert if taker has not been whitelisted', async () => { @@ -264,7 +265,7 @@ describe('Exchange transactions', () => { orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder); const takerAssetFillAmount = signedOrder.takerAssetAmount; const salt = generatePseudoRandomSalt(); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( whitelist.fillOrderIfWhitelisted.sendTransactionAsync( orderWithoutExchangeAddress, takerAssetFillAmount, @@ -272,7 +273,7 @@ describe('Exchange transactions', () => { signedOrder.signature, { from: takerAddress }, ), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should fill the order if maker and taker have been whitelisted', async () => { diff --git a/packages/contracts/test/exchange/wrapper.ts b/packages/contracts/test/exchange/wrapper.ts index 583ec9f913..8ef966be87 100644 --- a/packages/contracts/test/exchange/wrapper.ts +++ b/packages/contracts/test/exchange/wrapper.ts @@ -15,6 +15,7 @@ import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange'; import { TokenRegistryContract } from '../../src/contract_wrappers/generated/token_registry'; import { artifacts } from '../../src/utils/artifacts'; +import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; @@ -172,8 +173,8 @@ describe('Exchange wrappers', () => { expirationTimeSeconds: new BigNumber(Math.floor((Date.now() - 10000) / 1000)), }); - return expect(exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress), ); }); @@ -184,8 +185,8 @@ describe('Exchange wrappers', () => { takerAssetFillAmount: signedOrder.takerAssetAmount.div(2), }); - return expect(exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + exchangeWrapper.fillOrKillOrderAsync(signedOrder, takerAddress), ); }); }); @@ -197,12 +198,16 @@ describe('Exchange wrappers', () => { takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18), }); const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2); + await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress, { takerAssetFillAmount, + // 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: 250000, }); const newBalances = await erc20Wrapper.getBalancesAsync(); - const makerAssetFilledAmount = takerAssetFillAmount .times(signedOrder.makerAssetAmount) .dividedToIntegerBy(signedOrder.takerAssetAmount); @@ -212,6 +217,7 @@ describe('Exchange wrappers', () => { const takerFee = signedOrder.takerFee .times(makerAssetFilledAmount) .dividedToIntegerBy(signedOrder.makerAssetAmount); + expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount), ); @@ -360,7 +366,13 @@ describe('Exchange wrappers', () => { expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); // Call Exchange const takerAssetFillAmount = signedOrder.takerAssetAmount; - await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress, { takerAssetFillAmount }); + await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress, { + takerAssetFillAmount, + // 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, + }); // Verify post-conditions const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId); expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress); @@ -485,11 +497,11 @@ describe('Exchange wrappers', () => { await exchangeWrapper.fillOrKillOrderAsync(signedOrders[0], takerAddress); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.batchFillOrKillOrdersAsync(signedOrders, takerAddress, { takerAssetFillAmounts, }), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); @@ -535,6 +547,10 @@ describe('Exchange wrappers', () => { await exchangeWrapper.batchFillOrdersNoThrowAsync(signedOrders, takerAddress, { takerAssetFillAmounts, + // 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: 600000, }); const newBalances = await erc20Wrapper.getBalancesAsync(); @@ -591,6 +607,10 @@ describe('Exchange wrappers', () => { const newOrders = [invalidOrder, ...validOrders]; await exchangeWrapper.batchFillOrdersNoThrowAsync(newOrders, takerAddress, { takerAssetFillAmounts, + // 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: 450000, }); const newBalances = await erc20Wrapper.getBalancesAsync(); @@ -679,11 +699,11 @@ describe('Exchange wrappers', () => { orderFactory.newSignedOrder(), ]; - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.marketSellOrdersAsync(signedOrders, takerAddress, { takerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18), }), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); @@ -753,6 +773,10 @@ describe('Exchange wrappers', () => { }); await exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, { takerAssetFillAmount, + // 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: 600000, }); const newBalances = await erc20Wrapper.getBalancesAsync(); @@ -768,11 +792,11 @@ describe('Exchange wrappers', () => { orderFactory.newSignedOrder(), ]; - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, { takerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18), }), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); @@ -857,11 +881,11 @@ describe('Exchange wrappers', () => { orderFactory.newSignedOrder(), ]; - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.marketBuyOrdersAsync(signedOrders, takerAddress, { makerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18), }), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); @@ -931,6 +955,10 @@ describe('Exchange wrappers', () => { }); await exchangeWrapper.marketSellOrdersNoThrowAsync(signedOrders, takerAddress, { takerAssetFillAmount, + // 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: 600000, }); const newBalances = await erc20Wrapper.getBalancesAsync(); @@ -946,11 +974,11 @@ describe('Exchange wrappers', () => { orderFactory.newSignedOrder(), ]; - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( exchangeWrapper.marketBuyOrdersNoThrowAsync(signedOrders, takerAddress, { makerAssetFillAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), 18), }), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); diff --git a/packages/contracts/test/libraries/lib_bytes.ts b/packages/contracts/test/libraries/lib_bytes.ts index 26cfa8291c..1564631309 100644 --- a/packages/contracts/test/libraries/lib_bytes.ts +++ b/packages/contracts/test/libraries/lib_bytes.ts @@ -10,6 +10,7 @@ import * as Web3 from 'web3'; import { TestLibBytesContract } from '../../src/contract_wrappers/generated/test_lib_bytes'; import { artifacts } from '../../src/utils/artifacts'; +import { expectRevertOrAlwaysFailingTransactionAsync, expectRevertOrOtherErrorAsync } from '../../src/utils/assertions'; import { chaiSetup } from '../../src/utils/chai_setup'; import { constants } from '../../src/utils/constants'; import { provider, txDefaults, web3Wrapper } from '../../src/utils/web3_wrapper'; @@ -63,7 +64,10 @@ describe('LibBytes', () => { describe('popByte', () => { it('should revert if length is 0', async () => { - return expect(libBytes.publicPopByte.callAsync(constants.NULL_BYTES)).to.be.rejectedWith(constants.REVERT); + return expectRevertOrOtherErrorAsync( + libBytes.publicPopByte.callAsync(constants.NULL_BYTES), + constants.LIB_BYTES_GT_ZERO_LENGTH_REQUIRED, + ); }); it('should pop the last byte from the input and return it', async () => { @@ -77,8 +81,9 @@ describe('LibBytes', () => { describe('popAddress', () => { it('should revert if length is less than 20', async () => { - return expect(libBytes.publicPopAddress.callAsync(byteArrayShorterThan20Bytes)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrOtherErrorAsync( + libBytes.publicPopAddress.callAsync(byteArrayShorterThan20Bytes), + constants.LIB_BYTES_GTE_20_LENGTH_REQUIRED, ); }); @@ -162,16 +167,18 @@ describe('LibBytes', () => { it('should fail if the byte array is too short to hold an address)', async () => { const shortByteArray = '0xabcdef'; const offset = new BigNumber(0); - return expect(libBytes.publicReadAddress.callAsync(shortByteArray, offset)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrOtherErrorAsync( + libBytes.publicReadAddress.callAsync(shortByteArray, offset), + constants.LIB_BYTES_GTE_20_LENGTH_REQUIRED, ); }); it('should fail if the length between the offset and end of the byte array is too short to hold an address)', async () => { const byteArray = ethUtil.addHexPrefix(testAddress); const badOffset = new BigNumber(ethUtil.toBuffer(byteArray).byteLength); - return expect(libBytes.publicReadAddress.callAsync(byteArray, badOffset)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrOtherErrorAsync( + libBytes.publicReadAddress.callAsync(byteArray, badOffset), + constants.LIB_BYTES_GTE_20_LENGTH_REQUIRED, ); }); }); @@ -206,15 +213,17 @@ describe('LibBytes', () => { it('should fail if the byte array is too short to hold a bytes32)', async () => { const offset = new BigNumber(0); - return expect(libBytes.publicReadBytes32.callAsync(byteArrayShorterThan32Bytes, offset)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrOtherErrorAsync( + libBytes.publicReadBytes32.callAsync(byteArrayShorterThan32Bytes, offset), + constants.LIB_BYTES_GTE_32_LENGTH_REQUIRED, ); }); it('should fail if the length between the offset and end of the byte array is too short to hold a bytes32)', async () => { const badOffset = new BigNumber(ethUtil.toBuffer(testBytes32).byteLength); - return expect(libBytes.publicReadBytes32.callAsync(testBytes32, badOffset)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrOtherErrorAsync( + libBytes.publicReadBytes32.callAsync(testBytes32, badOffset), + constants.LIB_BYTES_GTE_32_LENGTH_REQUIRED, ); }); }); @@ -253,8 +262,9 @@ describe('LibBytes', () => { it('should fail if the byte array is too short to hold a uint256)', async () => { const offset = new BigNumber(0); - return expect(libBytes.publicReadUint256.callAsync(byteArrayShorterThan32Bytes, offset)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrOtherErrorAsync( + libBytes.publicReadUint256.callAsync(byteArrayShorterThan32Bytes, offset), + constants.LIB_BYTES_GTE_32_LENGTH_REQUIRED, ); }); @@ -263,8 +273,9 @@ describe('LibBytes', () => { const testUint256AsBuffer = ethUtil.toBuffer(formattedTestUint256); const byteArray = ethUtil.bufferToHex(testUint256AsBuffer); const badOffset = new BigNumber(testUint256AsBuffer.byteLength); - return expect(libBytes.publicReadUint256.callAsync(byteArray, badOffset)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrOtherErrorAsync( + libBytes.publicReadUint256.callAsync(byteArray, badOffset), + constants.LIB_BYTES_GTE_32_LENGTH_REQUIRED, ); }); }); @@ -281,10 +292,12 @@ describe('LibBytes', () => { */ describe('readFirst4', () => { + // AssertionError: expected promise to be rejected with an error including 'revert' but it was fulfilled with '0x08c379a0' it('should revert if byte array has a length < 4', async () => { const byteArrayLessThan4Bytes = '0x010101'; - return expect(libBytes.publicReadFirst4.callAsync(byteArrayLessThan4Bytes)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrOtherErrorAsync( + libBytes.publicReadFirst4.callAsync(byteArrayLessThan4Bytes), + constants.LIB_BYTES_GTE_4_LENGTH_REQUIRED, ); }); it('should return the first 4 bytes of a byte array of arbitrary length', async () => { diff --git a/packages/contracts/test/multi_sig_with_time_lock.ts b/packages/contracts/test/multi_sig_with_time_lock.ts index 1302d0fa09..9a673c7ca4 100644 --- a/packages/contracts/test/multi_sig_with_time_lock.ts +++ b/packages/contracts/test/multi_sig_with_time_lock.ts @@ -12,8 +12,10 @@ import { SubmissionContractEventArgs, } from '../src/contract_wrappers/generated/multi_sig_wallet_with_time_lock'; import { artifacts } from '../src/utils/artifacts'; +import { expectRevertOrAlwaysFailingTransactionAsync } from '../src/utils/assertions'; import { chaiSetup } from '../src/utils/chai_setup'; import { constants } from '../src/utils/constants'; +import { increaseTimeAndMineBlockAsync } from '../src/utils/increase_time'; import { MultiSigWrapper } from '../src/utils/multi_sig_wrapper'; import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper'; @@ -69,9 +71,9 @@ describe('MultiSigWalletWithTimeLock', () => { }); it('should throw when not called by wallet', async () => { - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( multiSig.changeTimeLock.sendTransactionAsync(SECONDS_TIME_LOCKED, { from: owners[0] }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw without enough confirmations', async () => { @@ -80,10 +82,9 @@ describe('MultiSigWalletWithTimeLock', () => { const res = await multiSigWrapper.submitTransactionAsync(destination, changeTimeLockData, owners[0]); const log = res.logs[0] as LogWithDecodedArgs; const txId = log.args.transactionId; - - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should set confirmation time with enough confirmations', async () => { @@ -148,14 +149,15 @@ describe('MultiSigWalletWithTimeLock', () => { txId = log.args.transactionId; await multiSigWrapper.confirmTransactionAsync(txId, owners[1]); }); + it('should throw if it has enough confirmations but is not past the time lock', async () => { - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should execute if it has enough confirmations and is past the time lock', async () => { - await web3Wrapper.increaseTimeAsync(SECONDS_TIME_LOCKED.toNumber()); + await increaseTimeAndMineBlockAsync(SECONDS_TIME_LOCKED.toNumber()); await web3Wrapper.awaitTransactionSuccessAsync( await multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }), constants.AWAIT_TRANSACTION_MINED_MS, diff --git a/packages/contracts/test/token_registry.ts b/packages/contracts/test/token_registry.ts index 64caac3870..eccb9222c0 100644 --- a/packages/contracts/test/token_registry.ts +++ b/packages/contracts/test/token_registry.ts @@ -9,6 +9,7 @@ import * as Web3 from 'web3'; import { TokenRegistryContract } from '../src/contract_wrappers/generated/token_registry'; import { artifacts } from '../src/utils/artifacts'; +import { expectRevertOrAlwaysFailingTransactionAsync } from '../src/utils/assertions'; import { chaiSetup } from '../src/utils/chai_setup'; import { constants } from '../src/utils/constants'; import { TokenRegWrapper } from '../src/utils/token_registry_wrapper'; @@ -76,7 +77,7 @@ describe('TokenRegistry', () => { describe('addToken', () => { it('should throw when not called by owner', async () => { - return expect(tokenRegWrapper.addTokenAsync(token1, notOwner)).to.be.rejectedWith(constants.REVERT); + return expectRevertOrAlwaysFailingTransactionAsync(tokenRegWrapper.addTokenAsync(token1, notOwner)); }); it('should add token metadata when called by owner', async () => { @@ -88,19 +89,19 @@ describe('TokenRegistry', () => { it('should throw if token already exists', async () => { await tokenRegWrapper.addTokenAsync(token1, owner); - return expect(tokenRegWrapper.addTokenAsync(token1, owner)).to.be.rejectedWith(constants.REVERT); + return expectRevertOrAlwaysFailingTransactionAsync(tokenRegWrapper.addTokenAsync(token1, owner)); }); it('should throw if token address is null', async () => { - return expect(tokenRegWrapper.addTokenAsync(nullToken, owner)).to.be.rejectedWith(constants.REVERT); + return expectRevertOrAlwaysFailingTransactionAsync(tokenRegWrapper.addTokenAsync(nullToken, owner)); }); it('should throw if name already exists', async () => { await tokenRegWrapper.addTokenAsync(token1, owner); const duplicateNameToken = _.assign({}, token2, { name: token1.name }); - return expect(tokenRegWrapper.addTokenAsync(duplicateNameToken, owner)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + tokenRegWrapper.addTokenAsync(duplicateNameToken, owner), ); }); @@ -110,8 +111,8 @@ describe('TokenRegistry', () => { symbol: token1.symbol, }); - return expect(tokenRegWrapper.addTokenAsync(duplicateSymbolToken, owner)).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrAlwaysFailingTransactionAsync( + tokenRegWrapper.addTokenAsync(duplicateSymbolToken, owner), ); }); }); @@ -137,9 +138,9 @@ describe('TokenRegistry', () => { describe('setTokenName', () => { it('should throw when not called by owner', async () => { - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( tokenReg.setTokenName.sendTransactionAsync(token1.address, token2.name, { from: notOwner }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should change the token name when called by owner', async () => { @@ -163,25 +164,25 @@ describe('TokenRegistry', () => { it('should throw if the name already exists', async () => { await tokenRegWrapper.addTokenAsync(token2, owner); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( tokenReg.setTokenName.sendTransactionAsync(token1.address, token2.name, { from: owner }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw if token does not exist', async () => { - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( tokenReg.setTokenName.sendTransactionAsync(nullToken.address, token2.name, { from: owner }), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); describe('setTokenSymbol', () => { it('should throw when not called by owner', async () => { - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( tokenReg.setTokenSymbol.sendTransactionAsync(token1.address, token2.symbol, { from: notOwner, }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should change the token symbol when called by owner', async () => { @@ -203,28 +204,28 @@ describe('TokenRegistry', () => { it('should throw if the symbol already exists', async () => { await tokenRegWrapper.addTokenAsync(token2, owner); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( tokenReg.setTokenSymbol.sendTransactionAsync(token1.address, token2.symbol, { from: owner, }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw if token does not exist', async () => { - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( tokenReg.setTokenSymbol.sendTransactionAsync(nullToken.address, token2.symbol, { from: owner, }), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); describe('removeToken', () => { it('should throw if not called by owner', async () => { const index = new BigNumber(0); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( tokenReg.removeToken.sendTransactionAsync(token1.address, index, { from: notOwner }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should remove token metadata when called by owner', async () => { @@ -241,17 +242,17 @@ describe('TokenRegistry', () => { it('should throw if token does not exist', async () => { const index = new BigNumber(0); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( tokenReg.removeToken.sendTransactionAsync(nullToken.address, index, { from: owner }), - ).to.be.rejectedWith(constants.REVERT); + ); }); it('should throw if token at given index does not match address', async () => { await tokenRegWrapper.addTokenAsync(token2, owner); const incorrectIndex = new BigNumber(0); - return expect( + return expectRevertOrAlwaysFailingTransactionAsync( tokenReg.removeToken.sendTransactionAsync(token2.address, incorrectIndex, { from: owner }), - ).to.be.rejectedWith(constants.REVERT); + ); }); }); }); diff --git a/packages/contracts/test/unlimited_allowance_token.ts b/packages/contracts/test/unlimited_allowance_token.ts index b2acdebaaa..9aa0a040a6 100644 --- a/packages/contracts/test/unlimited_allowance_token.ts +++ b/packages/contracts/test/unlimited_allowance_token.ts @@ -7,6 +7,7 @@ import * as Web3 from 'web3'; import { DummyERC20TokenContract } from '../src/contract_wrappers/generated/dummy_e_r_c20_token'; import { artifacts } from '../src/utils/artifacts'; +import { expectRevertOrAlwaysFailingTransactionAsync, expectRevertOrOtherErrorAsync } from '../src/utils/assertions'; import { chaiSetup } from '../src/utils/chai_setup'; import { constants } from '../src/utils/constants'; import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper'; @@ -55,8 +56,9 @@ describe('UnlimitedAllowanceToken', () => { it('should throw if owner has insufficient balance', async () => { const ownerBalance = await token.balanceOf.callAsync(owner); const amountToTransfer = ownerBalance.plus(1); - return expect(token.transfer.callAsync(spender, amountToTransfer, { from: owner })).to.be.rejectedWith( - constants.REVERT, + return expectRevertOrOtherErrorAsync( + token.transfer.callAsync(spender, amountToTransfer, { from: owner }), + constants.ERC20_INSUFFICIENT_BALANCE, ); }); @@ -93,11 +95,12 @@ describe('UnlimitedAllowanceToken', () => { await token.approve.sendTransactionAsync(spender, amountToTransfer, { from: owner }), constants.AWAIT_TRANSACTION_MINED_MS, ); - return expect( + return expectRevertOrOtherErrorAsync( token.transferFrom.callAsync(owner, spender, amountToTransfer, { from: spender, }), - ).to.be.rejectedWith(constants.REVERT); + constants.ERC20_INSUFFICIENT_BALANCE, + ); }); it('should throw if spender has insufficient allowance', async () => { @@ -108,11 +111,12 @@ describe('UnlimitedAllowanceToken', () => { const isSpenderAllowanceInsufficient = spenderAllowance.cmp(amountToTransfer) < 0; expect(isSpenderAllowanceInsufficient).to.be.true(); - return expect( + return expectRevertOrOtherErrorAsync( token.transferFrom.callAsync(owner, spender, amountToTransfer, { from: spender, }), - ).to.be.rejectedWith(constants.REVERT); + constants.ERC20_INSUFFICIENT_ALLOWANCE, + ); }); it('should return true on a 0 value transfer', async () => { diff --git a/packages/dev-utils/CHANGELOG.json b/packages/dev-utils/CHANGELOG.json index ecb43a42e4..4e1647cd84 100644 --- a/packages/dev-utils/CHANGELOG.json +++ b/packages/dev-utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "0.4.3", + "changes": [ + { + "note": "Add optional parameter shouldUseFakeGasEstimate to Web3Config", + "pr": 622 + } + ] + }, { "version": "0.4.2", "changes": [ diff --git a/packages/dev-utils/src/blockchain_lifecycle.ts b/packages/dev-utils/src/blockchain_lifecycle.ts index 3e35de8619..8e4ad81c75 100644 --- a/packages/dev-utils/src/blockchain_lifecycle.ts +++ b/packages/dev-utils/src/blockchain_lifecycle.ts @@ -1,6 +1,12 @@ -import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { uniqueVersionIds, Web3Wrapper } from '@0xproject/web3-wrapper'; +import * as _ from 'lodash'; import * as Web3 from 'web3'; +enum NodeType { + Geth = 'GETH', + Ganache = 'GANACHE', +} + export class BlockchainLifecycle { private _web3Wrapper: Web3Wrapper; private _snapshotIdsStack: number[]; @@ -8,17 +14,47 @@ export class BlockchainLifecycle { this._web3Wrapper = web3Wrapper; this._snapshotIdsStack = []; } - // TODO: In order to run these tests on an actual node, we should check if we are running against - // TestRPC, if so, use snapshots, otherwise re-deploy contracts before every test public async startAsync(): Promise { - const snapshotId = await this._web3Wrapper.takeSnapshotAsync(); - this._snapshotIdsStack.push(snapshotId); + const nodeType = await this._getNodeTypeAsync(); + switch (nodeType) { + case NodeType.Ganache: + const snapshotId = await this._web3Wrapper.takeSnapshotAsync(); + this._snapshotIdsStack.push(snapshotId); + break; + case NodeType.Geth: + const blockNumber = await this._web3Wrapper.getBlockNumberAsync(); + this._snapshotIdsStack.push(blockNumber); + break; + default: + throw new Error(`Unknown node type: ${nodeType}`); + } } public async revertAsync(): Promise { - const snapshotId = this._snapshotIdsStack.pop() as number; - const didRevert = await this._web3Wrapper.revertSnapshotAsync(snapshotId); - if (!didRevert) { - throw new Error(`Snapshot with id #${snapshotId} failed to revert`); + const nodeType = await this._getNodeTypeAsync(); + switch (nodeType) { + case NodeType.Ganache: + const snapshotId = this._snapshotIdsStack.pop() as number; + const didRevert = await this._web3Wrapper.revertSnapshotAsync(snapshotId); + if (!didRevert) { + throw new Error(`Snapshot with id #${snapshotId} failed to revert`); + } + break; + case NodeType.Geth: + const blockNumber = this._snapshotIdsStack.pop() as number; + await this._web3Wrapper.setHeadAsync(blockNumber); + break; + default: + throw new Error(`Unknown node type: ${nodeType}`); + } + } + private async _getNodeTypeAsync(): Promise { + const version = await this._web3Wrapper.getNodeVersionAsync(); + if (_.includes(version, uniqueVersionIds.geth)) { + return NodeType.Geth; + } else if (_.includes(version, uniqueVersionIds.ganache)) { + return NodeType.Ganache; + } else { + throw new Error(`Unknown client version: ${version}`); } } } diff --git a/packages/dev-utils/src/web3_factory.ts b/packages/dev-utils/src/web3_factory.ts index 12872c122b..25201228d1 100644 --- a/packages/dev-utils/src/web3_factory.ts +++ b/packages/dev-utils/src/web3_factory.ts @@ -19,16 +19,22 @@ export interface Web3Config { hasAddresses?: boolean; // default: true shouldUseInProcessGanache?: boolean; // default: false rpcUrl?: string; // default: localhost:8545 + shouldUseFakeGasEstimate?: boolean; // default: true } export const web3Factory = { getRpcProvider(config: Web3Config = {}): ProviderEngine { const provider = new ProviderEngine(); const hasAddresses = _.isUndefined(config.hasAddresses) || config.hasAddresses; + config.shouldUseFakeGasEstimate = + _.isUndefined(config.shouldUseFakeGasEstimate) || config.shouldUseFakeGasEstimate; if (!hasAddresses) { provider.addProvider(new EmptyWalletSubprovider()); } - provider.addProvider(new FakeGasEstimateSubprovider(constants.GAS_LIMIT)); + + if (config.shouldUseFakeGasEstimate) { + provider.addProvider(new FakeGasEstimateSubprovider(constants.GAS_LIMIT)); + } const logger = { log: (arg: any) => { fs.appendFileSync('ganache.log', `${arg}\n`); diff --git a/packages/devnet/Dockerfile b/packages/devnet/Dockerfile new file mode 100644 index 0000000000..8e276d605b --- /dev/null +++ b/packages/devnet/Dockerfile @@ -0,0 +1,25 @@ +FROM alpine:3.7 + +RUN \ + apk add --update go git make gcc musl-dev linux-headers ca-certificates && \ + # TODO(albrow): Change the Git URL and branch once we have all relvant PRs + # merged to upstream. + git clone --depth 1 --branch '0x-testing' https://github.com/0xProject/go-ethereum && \ + (cd go-ethereum && make geth) && \ + cp go-ethereum/build/bin/geth /geth && \ + apk del go git make gcc musl-dev linux-headers && \ + rm -rf /go-ethereum && rm -rf /var/cache/apk/* + +RUN mkdir ~/devnet +WORKDIR ~/devnet + +COPY genesis.json . +COPY node0/ ./node0 +COPY run.sh . + +RUN /geth --datadir node0/ init genesis.json + +EXPOSE 8501 +EXPOSE 30310 + +ENTRYPOINT ./run.sh diff --git a/packages/devnet/README.md b/packages/devnet/README.md new file mode 100644 index 0000000000..f0b165cbd4 --- /dev/null +++ b/packages/devnet/README.md @@ -0,0 +1,110 @@ +## 0x Devnet + +A private, single-node PoA Ethereum network for testing purposes only. It uses +Geth and the PoA implementation called "Clique". + +## Installation + +The devnet requires Docker to run (the latest version is recommended). + +In the package root directory, run: + +``` +docker build -t 0x-devnet . +``` + +## Usage + +To start the network, run: + +``` +docker run -it --rm -p 8501:8501 0x-devnet +``` + +Depending on your OS and how you installed docker, you may need to prefix any +docker commands with `sudo`. + +The Docker container exposes the JSON RPC API at port 8501, and this is the +primary way you are expected to interact with the devnet. The following +endpoints are supported: `personal,db,eth,net,web3,txpool,miner,debug`. + +You can stop the network with `docker stop` and it will automatically clean up +after itself. (`docker stop` typically requires you to use `docker ps` to find +the name of the currently running container). + +### Configuration + +The devnet network only has a single node and uses PoA instead of PoW. That +means that one node, called the "sealer", is the ultimate authority for +validating transactions and adding new blocks to the chain. Since there is no +PoW it also means that mining does not require significant computational +resources. You can learn more about PoA and the Geth-specific implementation +called "Clique" in [EIP-225](https://github.com/ethereum/EIPs/issues/225). + +The address of the "sealer" is `0xe8816898d851d5b61b7f950627d04d794c07ca37`. The +password associated with the account is "password" and the (encrypted) private +keys are visible in the **node0/keystore** directory. This account is already +"unlocked" in the Geth node by default, so you can do things like sign and send +transactions from this account using the JSON RPC endpoints directly. + +There are also a number of other addresses that have hard-coded starting +balances for testing purposes. You can see the details in the **genesis.json** +file. All of these accounts are also unlocked by default. + +### Additional JSON RPC Methods + +In addition to the +[standard JSON RPC methods](https://github.com/ethereum/wiki/wiki/JSON-RPC) and +the +[Geth Management API](https://github.com/ethereum/go-ethereum/wiki/Management-APIs) +The devnet node supports some additional JSON RPC methods: + +#### debug_increaseTime + +Increases the timestamp of the next mined block. + +##### Parameters + +`Number` - The number of seconds by which to increase the time offset. + +##### Returns + +`Number` - The total number of seconds by which the time offset has been +increased (this includes all calls to `debug_increaseTime`). + +##### Example + +```js +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"debug_increaseTime","params":[100],"id":67}' + +// Result +{ + "id":67, + "jsonrpc": "2.0", + "result": "5000" +} +``` + +### Mining + +The node will automatically (nearly instantly) mine a block whenever new +transactions are added to the transaction pool. If there are no transactions in +the pool, it will wait. + +To stop mining, use the +[`miner.stop`](https://github.com/ethereum/go-ethereum/wiki/Management-APIs#miner_stop) +method. + +To start mining again, you can use the +[`miner.start`](https://github.com/ethereum/go-ethereum/wiki/Management-APIs#miner_start) +JSON RPC method. + +## Contributing + +We strongly recommend that the community help us make improvements and determine +the future direction of the protocol. To report bugs within this package, please +create an issue in this repository. + +Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting +started. diff --git a/packages/devnet/genesis.json b/packages/devnet/genesis.json new file mode 100644 index 0000000000..90431c31ce --- /dev/null +++ b/packages/devnet/genesis.json @@ -0,0 +1,61 @@ +{ + "config": { + "chainId": 50, + "homesteadBlock": 1, + "eip150Block": 2, + "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "eip155Block": 3, + "eip158Block": 3, + "byzantiumBlock": 4, + "clique": { + "period": 0, + "epoch": 30000 + } + }, + "nonce": "0x0", + "timestamp": "0x5af1ffac", + "extraData": + "0x0000000000000000000000000000000000000000000000000000000000000000e8816898d851d5b61b7f950627d04d794c07ca370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x4c4b400000", + "difficulty": "0x1", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "0xe8816898d851d5b61b7f950627d04d794c07ca37": { + "balance": "0x56BC75E2D63100000" + }, + "0x5409ed021d9299bf6814279a6a1411a7e866a631": { + "balance": "0x56BC75E2D63100000" + }, + "0x6ecbe1db9ef729cbe972c83fb886247691fb6beb": { + "balance": "0x56BC75E2D63100000" + }, + "0xe36ea790bc9d7ab70c55260c66d52b1eca985f84": { + "balance": "0x56BC75E2D63100000" + }, + "0xe834ec434daba538cd1b9fe1582052b880bd7e63": { + "balance": "0x56BC75E2D63100000" + }, + "0x78dc5d2d739606d31509c31d654056a45185ecb6": { + "balance": "0x56BC75E2D63100000" + }, + "0xa8dda8d7f5310e4a9e24f8eba77e091ac264f872": { + "balance": "0x56BC75E2D63100000" + }, + "0x06cef8e666768cc40cc78cf93d9611019ddcb628": { + "balance": "0x56BC75E2D63100000" + }, + "0x4404ac8bd8f9618d27ad2f1485aa1b2cfd82482d": { + "balance": "0x56BC75E2D63100000" + }, + "0x7457d5e02197480db681d3fdf256c7aca21bdc12": { + "balance": "0x56BC75E2D63100000" + }, + "0x91c987bf62d25945db517bdaa840a6c661374402": { + "balance": "0x56BC75E2D63100000" + } + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} diff --git a/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-08.903003751Z--5409ed021d9299bf6814279a6a1411a7e866a631 b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-08.903003751Z--5409ed021d9299bf6814279a6a1411a7e866a631 new file mode 100644 index 0000000000..32c4002e04 --- /dev/null +++ b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-08.903003751Z--5409ed021d9299bf6814279a6a1411a7e866a631 @@ -0,0 +1 @@ +{"address":"5409ed021d9299bf6814279a6a1411a7e866a631","crypto":{"cipher":"aes-128-ctr","ciphertext":"7c7bdd62b303eb3a42d5d8e935825ed5a05a47cb2cef71e346c61b1bd582f1aa","cipherparams":{"iv":"7fd6c9d9f9893f2c480735b5386b6d75"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"79cc86edc3a668845a68fabb3913710b7504922e47aac8513ab3d6a28d090218"},"mac":"8a593ae0d0b964e47625bc964b6d389f5687f5bde631b4913136db4ab1b8083e"},"id":"29f637ba-6a65-4401-a0d1-30e1554bd776","version":3} diff --git a/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-09.794553183Z--6ecbe1db9ef729cbe972c83fb886247691fb6beb b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-09.794553183Z--6ecbe1db9ef729cbe972c83fb886247691fb6beb new file mode 100644 index 0000000000..ba84ccfd26 --- /dev/null +++ b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-09.794553183Z--6ecbe1db9ef729cbe972c83fb886247691fb6beb @@ -0,0 +1 @@ +{"address":"6ecbe1db9ef729cbe972c83fb886247691fb6beb","crypto":{"cipher":"aes-128-ctr","ciphertext":"ecaf4f2839d74d92e2cb87c2fc7d52862661b46e697d70acfbe43f0893db73ed","cipherparams":{"iv":"7641c3a107228f8a901c07a07ea1f70d"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"c67c9fb30648df6985c0490b6603382147e7dc1ea28ca8c934af4a453ec0555b"},"mac":"985dca9ce65ad400fa4c9009742be2d409f402fe05203fc1278cfd1451729e8d"},"id":"e8634edc-08e6-415e-8d65-7985c4c4a05c","version":3} diff --git a/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-10.696351411Z--e36ea790bc9d7ab70c55260c66d52b1eca985f84 b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-10.696351411Z--e36ea790bc9d7ab70c55260c66d52b1eca985f84 new file mode 100644 index 0000000000..e889c38b34 --- /dev/null +++ b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-10.696351411Z--e36ea790bc9d7ab70c55260c66d52b1eca985f84 @@ -0,0 +1 @@ +{"address":"e36ea790bc9d7ab70c55260c66d52b1eca985f84","crypto":{"cipher":"aes-128-ctr","ciphertext":"49f89d7d612049f5f3581fc7c97d32ec9c9a2ca3c11165587139f16bfb29de6b","cipherparams":{"iv":"9767e0687a097c5b57e9cb30eec9bc0a"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"3e8f23332df99d519b602a0f6f4724338ba3fd9e7e313c337a92ffd1cafa19f1"},"mac":"4892051a669d45bb7de32a5eab63ee8fe52485a02218ce1806515da2adbd6584"},"id":"3488ad36-4a9d-4282-8651-7939b822429d","version":3} diff --git a/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-11.479938556Z--e834ec434daba538cd1b9fe1582052b880bd7e63 b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-11.479938556Z--e834ec434daba538cd1b9fe1582052b880bd7e63 new file mode 100644 index 0000000000..c12742c542 --- /dev/null +++ b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-11.479938556Z--e834ec434daba538cd1b9fe1582052b880bd7e63 @@ -0,0 +1 @@ +{"address":"e834ec434daba538cd1b9fe1582052b880bd7e63","crypto":{"cipher":"aes-128-ctr","ciphertext":"a8ae3896739c63fc3bfe034277f6a1924a1c0ddc3f6747391dada8e61e15a928","cipherparams":{"iv":"f4f4d786cd3650a428a8bac5a6c824b1"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"9acecc321bcab9b69ffdea494b8894ad0221c30f05c17d2302e315db8708ecc6"},"mac":"fc416b8f539fdc1e39e87a3bd2a69b04455875de701ced60cc8948b222171380"},"id":"0d9703e8-14fc-45d0-a425-2c40b8ae846a","version":3} diff --git a/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-12.260348580Z--78dc5d2d739606d31509c31d654056a45185ecb6 b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-12.260348580Z--78dc5d2d739606d31509c31d654056a45185ecb6 new file mode 100644 index 0000000000..691e31dcff --- /dev/null +++ b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-12.260348580Z--78dc5d2d739606d31509c31d654056a45185ecb6 @@ -0,0 +1 @@ +{"address":"78dc5d2d739606d31509c31d654056a45185ecb6","crypto":{"cipher":"aes-128-ctr","ciphertext":"25e90e593f08e9e3adc426c8685d90db5d1c04957e9dc8d5fab4ae30c3306b61","cipherparams":{"iv":"72ece22297a27363e795b678bcbd6be5"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"2201502b9d3c4e2076d9d15bfd9da3a6c75d9e2e574aabb29c3bc5a3b5ec55a5"},"mac":"13d709ed4bd2f5bf4973fc1373f8434835f0d12dc99b32c6fc14d9df7f41c62d"},"id":"3902dff4-5681-4646-b825-849f96efeec5","version":3} diff --git a/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-13.178294829Z--a8dda8d7f5310e4a9e24f8eba77e091ac264f872 b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-13.178294829Z--a8dda8d7f5310e4a9e24f8eba77e091ac264f872 new file mode 100644 index 0000000000..9054e0019b --- /dev/null +++ b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-13.178294829Z--a8dda8d7f5310e4a9e24f8eba77e091ac264f872 @@ -0,0 +1 @@ +{"address":"a8dda8d7f5310e4a9e24f8eba77e091ac264f872","crypto":{"cipher":"aes-128-ctr","ciphertext":"0d67c13cf0b130e8ffa1aaca5df372f727164e633f8e0e28a3e54d0884ffb568","cipherparams":{"iv":"619cd539cda9f40abb45bba00b5fe53d"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"4effcd9b6fe71ee31cfe9057290154329b9af3acb6dcc46be7f78b5b9dcd3f42"},"mac":"c6eecd25944f4250b7b875d76bfbb60cc4e8db1d081621d1a2ddb72ea4e52a6d"},"id":"556bd3f1-1e5b-47a4-9b6e-448b9989d7d3","version":3} diff --git a/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-13.960499696Z--06cef8e666768cc40cc78cf93d9611019ddcb628 b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-13.960499696Z--06cef8e666768cc40cc78cf93d9611019ddcb628 new file mode 100644 index 0000000000..0870638dde --- /dev/null +++ b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-13.960499696Z--06cef8e666768cc40cc78cf93d9611019ddcb628 @@ -0,0 +1 @@ +{"address":"06cef8e666768cc40cc78cf93d9611019ddcb628","crypto":{"cipher":"aes-128-ctr","ciphertext":"38c9ca150932dc8c5ec5c65796425b2de98295cae64db08b816da2c06fc52c20","cipherparams":{"iv":"512127e8e606c481612473e7bc4d38f1"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"16c4cabfd13cae2df66d8ff9acc7f503c95c808b00d0bb6a12932203889c679b"},"mac":"52297b496e8751627dea1ee17bf5cbea1926f90bcde3ffc8baa089184672f875"},"id":"31102097-86e4-4e19-ad73-03c3de67bf3b","version":3} diff --git a/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-14.757010386Z--4404ac8bd8f9618d27ad2f1485aa1b2cfd82482d b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-14.757010386Z--4404ac8bd8f9618d27ad2f1485aa1b2cfd82482d new file mode 100644 index 0000000000..5f28db78f5 --- /dev/null +++ b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-14.757010386Z--4404ac8bd8f9618d27ad2f1485aa1b2cfd82482d @@ -0,0 +1 @@ +{"address":"4404ac8bd8f9618d27ad2f1485aa1b2cfd82482d","crypto":{"cipher":"aes-128-ctr","ciphertext":"ca7aedbacc960fc0fcb418606d7bdf042c36cc2808a5c94ac222cc0b44a9970d","cipherparams":{"iv":"3b1fe5da1cf5d6cd2ceaaf24c008c897"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"a94e4d41d77ff6dc54beda30c7a46d8f3cc312ebeffa0352d679f7e3fc5301dc"},"mac":"9a82bf60103d05878f8af3c07765c22cba3df9b1c4376eaf859e47b805666e42"},"id":"ab68c67b-e15a-4ade-b3d9-2180a32b28fe","version":3} diff --git a/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-15.554233052Z--7457d5e02197480db681d3fdf256c7aca21bdc12 b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-15.554233052Z--7457d5e02197480db681d3fdf256c7aca21bdc12 new file mode 100644 index 0000000000..2a2c0194ad --- /dev/null +++ b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-15.554233052Z--7457d5e02197480db681d3fdf256c7aca21bdc12 @@ -0,0 +1 @@ +{"address":"7457d5e02197480db681d3fdf256c7aca21bdc12","crypto":{"cipher":"aes-128-ctr","ciphertext":"720dcc2889c7b3636f9f659650181b0d46d82420460e23454277273f528baaee","cipherparams":{"iv":"1510028e2b9988d1a73b71cbb692d085"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"5db2b62f4d1f55a3f24c014c4f23f3ec9a2992dca6c2a89c24a566f99a079396"},"mac":"22c6fb134fd0a748195ea83e9ccb490ab2c9a3e8761f9d74ea6d02abbdeb8a43"},"id":"704c31f8-8ca2-4b49-9fdc-5923f5712dad","version":3} diff --git a/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-16.342711541Z--91c987bf62d25945db517bdaa840a6c661374402 b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-16.342711541Z--91c987bf62d25945db517bdaa840a6c661374402 new file mode 100644 index 0000000000..edc6d7531f --- /dev/null +++ b/packages/devnet/node0/keystore/UTC--2018-05-11T21-29-16.342711541Z--91c987bf62d25945db517bdaa840a6c661374402 @@ -0,0 +1 @@ +{"address":"91c987bf62d25945db517bdaa840a6c661374402","crypto":{"cipher":"aes-128-ctr","ciphertext":"8f461f3c74643f382f7fc1f71719d5a89ed8cf75854d8a1b53e133997b53a386","cipherparams":{"iv":"cf595fb7680d36b4f5a01599ee54d2d1"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"73a9e599369d2bfaedd044559415147240c3517f6cd1dec8f77a98993d1ceaf8"},"mac":"c8be4dc59ad28d40f7b549a6b72834d149c84d67dc35e687676bbee0e07be395"},"id":"21cca6fb-7876-4e39-a986-a0a37f90da6d","version":3} diff --git a/packages/devnet/node0/keystore/UTC--2018-05-15T21-50-24.532037737Z--e8816898d851d5b61b7f950627d04d794c07ca37 b/packages/devnet/node0/keystore/UTC--2018-05-15T21-50-24.532037737Z--e8816898d851d5b61b7f950627d04d794c07ca37 new file mode 100644 index 0000000000..cd2c97a886 --- /dev/null +++ b/packages/devnet/node0/keystore/UTC--2018-05-15T21-50-24.532037737Z--e8816898d851d5b61b7f950627d04d794c07ca37 @@ -0,0 +1 @@ +{"address":"e8816898d851d5b61b7f950627d04d794c07ca37","crypto":{"cipher":"aes-128-ctr","ciphertext":"1ff4add6955cba7ddaf29f66d7d21c5e1d714ef6191fbc651ae60f2ea3c95e8f","cipherparams":{"iv":"3ff869fbdbe1a523cdb327780365976e"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"7372dbae5fb318f8684902e099c311d4188721d677974d729711762c7ef6030c"},"mac":"485fa5dc701067782baa1589716a53110c7f917eb259e35ebca7265bbb7150b1"},"id":"89edb004-5b00-4607-a3af-a0d9ab9b1c34","version":3} \ No newline at end of file diff --git a/packages/devnet/node0/password.txt b/packages/devnet/node0/password.txt new file mode 100644 index 0000000000..9842d2661f --- /dev/null +++ b/packages/devnet/node0/password.txt @@ -0,0 +1,11 @@ +password +password +password +password +password +password +password +password +password +password +password diff --git a/packages/devnet/run.sh b/packages/devnet/run.sh new file mode 100755 index 0000000000..85d223c61c --- /dev/null +++ b/packages/devnet/run.sh @@ -0,0 +1,42 @@ +set -e + +# Create log directory for Geth +mkdir -p /var/log + +# Start Geth in background and redirect output to log file +/geth \ + --verbosity 5 \ + --datadir node0/ \ + --syncmode 'full' \ + --nat none \ + --nodiscover \ + --port 30310 \ + --txpool.journal '' \ + --rpc \ + --rpcaddr '0.0.0.0' \ + --rpcport 8501 \ + --rpcapi 'personal,db,eth,net,web3,txpool,miner,debug' \ + --networkid 50 \ + --gasprice '2000000000' \ + --targetgaslimit '0x4c4b400000' \ + --mine \ + --etherbase '0xe8816898d851d5b61b7f950627d04d794c07ca37' \ + --unlock '0xe8816898d851d5b61b7f950627d04d794c07ca37,0x5409ed021d9299bf6814279a6a1411a7e866a631,0x6ecbe1db9ef729cbe972c83fb886247691fb6beb,0xe36ea790bc9d7ab70c55260c66d52b1eca985f84,0xe834ec434daba538cd1b9fe1582052b880bd7e63,0x78dc5d2d739606d31509c31d654056a45185ecb6,0xa8dda8d7f5310e4a9e24f8eba77e091ac264f872,0x06cef8e666768cc40cc78cf93d9611019ddcb628,0x4404ac8bd8f9618d27ad2f1485aa1b2cfd82482d,0x7457d5e02197480db681d3fdf256c7aca21bdc12,0x91c987bf62d25945db517bdaa840a6c661374402' \ + --password=node0/password.txt \ + > /var/log/geth & + +# Wait for Geth to unlock the first account +sleep 10 + +# Send some transactions. +# HACK(albrow): 🐉 We have to do this so that debug.setHead works correctly. +# (Geth does not seem to like debug.setHead(0), so by sending some transactions +# we increase the current block number beyond 0). Additionally, some tests seem +# to break when there are fewer than 3 blocks in the chain. (We have no idea +# why, but it was consistently reproducible). +/geth --datadir node0/ attach --exec 'eth.sendTransaction({"from": "0x5409ED021D9299bf6814279A6A1411A7e866A631", "to": "0x84bd1cfa409cb0bb9b23b8b1a33515b4ac00a0af", "value": "0x1"})' +/geth --datadir node0/ attach --exec 'eth.sendTransaction({"from": "0x5409ED021D9299bf6814279A6A1411A7e866A631", "to": "0x84bd1cfa409cb0bb9b23b8b1a33515b4ac00a0af", "value": "0x1"})' +/geth --datadir node0/ attach --exec 'eth.sendTransaction({"from": "0x5409ED021D9299bf6814279A6A1411A7e866A631", "to": "0x84bd1cfa409cb0bb9b23b8b1a33515b4ac00a0af", "value": "0x1"})' + +# Use tail to re-attach to the log file and actually see the output. +tail -f /var/log/geth diff --git a/packages/migrations/artifacts/2.0.0/MultiSigWallet.json b/packages/migrations/artifacts/2.0.0/MultiSigWallet.json index fe775de065..8fc91c6197 100644 --- a/packages/migrations/artifacts/2.0.0/MultiSigWallet.json +++ b/packages/migrations/artifacts/2.0.0/MultiSigWallet.json @@ -1,591 +1,584 @@ { - "schemaVersion": "2.0.0", - "contractName": "MultiSigWallet", - "compilerOutput": { - "abi": [ - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "uint256" - } - ], - "name": "owners", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "owner", - "type": "address" - } - ], - "name": "removeOwner", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "transactionId", - "type": "uint256" - } - ], - "name": "revokeConfirmation", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "address" - } - ], - "name": "isOwner", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "uint256" - }, - { - "name": "", - "type": "address" - } - ], - "name": "confirmations", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "pending", - "type": "bool" - }, - { - "name": "executed", - "type": "bool" - } - ], - "name": "getTransactionCount", - "outputs": [ - { - "name": "count", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "owner", - "type": "address" - } - ], - "name": "addOwner", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "transactionId", - "type": "uint256" - } - ], - "name": "isConfirmed", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "transactionId", - "type": "uint256" - } - ], - "name": "getConfirmationCount", - "outputs": [ - { - "name": "count", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "uint256" - } - ], - "name": "transactions", - "outputs": [ - { - "name": "destination", - "type": "address" - }, - { - "name": "value", - "type": "uint256" - }, - { - "name": "data", - "type": "bytes" - }, - { - "name": "executed", - "type": "bool" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getOwners", - "outputs": [ - { - "name": "", - "type": "address[]" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "from", - "type": "uint256" - }, - { - "name": "to", - "type": "uint256" - }, - { - "name": "pending", - "type": "bool" - }, - { - "name": "executed", - "type": "bool" - } - ], - "name": "getTransactionIds", - "outputs": [ - { - "name": "_transactionIds", - "type": "uint256[]" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "transactionId", - "type": "uint256" - } - ], - "name": "getConfirmations", - "outputs": [ - { - "name": "_confirmations", - "type": "address[]" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "transactionCount", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_required", - "type": "uint256" - } - ], - "name": "changeRequirement", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "transactionId", - "type": "uint256" - } - ], - "name": "confirmTransaction", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "destination", - "type": "address" - }, - { - "name": "value", - "type": "uint256" - }, - { - "name": "data", - "type": "bytes" - } - ], - "name": "submitTransaction", - "outputs": [ - { - "name": "transactionId", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "MAX_OWNER_COUNT", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "required", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "owner", - "type": "address" - }, - { - "name": "newOwner", - "type": "address" - } - ], - "name": "replaceOwner", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "transactionId", - "type": "uint256" - } - ], - "name": "executeTransaction", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "name": "_owners", - "type": "address[]" - }, - { - "name": "_required", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "payable": true, - "stateMutability": "payable", - "type": "fallback" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "sender", - "type": "address" - }, - { - "indexed": true, - "name": "transactionId", - "type": "uint256" - } - ], - "name": "Confirmation", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "sender", - "type": "address" - }, - { - "indexed": true, - "name": "transactionId", - "type": "uint256" - } - ], - "name": "Revocation", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "transactionId", - "type": "uint256" - } - ], - "name": "Submission", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "transactionId", - "type": "uint256" - } - ], - "name": "Execution", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "transactionId", - "type": "uint256" - } - ], - "name": "ExecutionFailure", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "sender", - "type": "address" - }, - { - "indexed": false, - "name": "value", - "type": "uint256" - } - ], - "name": "Deposit", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "owner", - "type": "address" - } - ], - "name": "OwnerAddition", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "owner", - "type": "address" - } - ], - "name": "OwnerRemoval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "required", - "type": "uint256" - } - ], - "name": "RequirementChange", - "type": "event" - } - ], - "evm": { - "bytecode": { - "linkReferences": {}, - "object": - "0x60806040523480156200001157600080fd5b50604051620015f6380380620015f68339810160405280516020820151910180519091906000908260328211806200004857508181115b8062000052575080155b806200005c575081155b156200006757600080fd5b600092505b84518310156200013b576002600086858151811015156200008957fe5b6020908102909101810151600160a060020a031682528101919091526040016000205460ff1680620000dc57508483815181101515620000c557fe5b90602001906020020151600160a060020a03166000145b15620000e757600080fd5b6001600260008786815181101515620000fc57fe5b602090810291909101810151600160a060020a03168252810191909152604001600020805460ff1916911515919091179055600192909201916200006c565b84516200015090600390602088019062000162565b50505060049190915550620001f69050565b828054828255906000526020600020908101928215620001ba579160200282015b82811115620001ba5782518254600160a060020a031916600160a060020a0390911617825560209092019160019091019062000183565b50620001c8929150620001cc565b5090565b620001f391905b80821115620001c8578054600160a060020a0319168155600101620001d3565b90565b6113f080620002066000396000f3006080604052600436106101035763ffffffff60e060020a600035041663025e7c278114610145578063173825d91461017957806320ea8d861461019a5780632f54bf6e146101b25780633411c81c146101e7578063547415251461020b5780637065cb481461023c578063784547a71461025d5780638b51d13f146102755780639ace38c21461028d578063a0e67e2b14610348578063a8abe69a146103ad578063b5dc40c3146103d2578063b77bf600146103ea578063ba51a6df146103ff578063c01a8c8414610417578063c64274741461042f578063d74f8edd14610498578063dc8452cd146104ad578063e20056e6146104c2578063ee22610b146104e9575b60003411156101435760408051348152905133917fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c919081900360200190a25b005b34801561015157600080fd5b5061015d600435610501565b60408051600160a060020a039092168252519081900360200190f35b34801561018557600080fd5b50610143600160a060020a0360043516610529565b3480156101a657600080fd5b5061014360043561068e565b3480156101be57600080fd5b506101d3600160a060020a0360043516610748565b604080519115158252519081900360200190f35b3480156101f357600080fd5b506101d3600435600160a060020a036024351661075d565b34801561021757600080fd5b5061022a6004351515602435151561077d565b60408051918252519081900360200190f35b34801561024857600080fd5b50610143600160a060020a03600435166107e9565b34801561026957600080fd5b506101d36004356108e7565b34801561028157600080fd5b5061022a60043561096b565b34801561029957600080fd5b506102a56004356109da565b6040518085600160a060020a0316600160a060020a031681526020018481526020018060200183151515158152602001828103825284818151815260200191508051906020019080838360005b8381101561030a5781810151838201526020016102f2565b50505050905090810190601f1680156103375780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b34801561035457600080fd5b5061035d610a98565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610399578181015183820152602001610381565b505050509050019250505060405180910390f35b3480156103b957600080fd5b5061035d60043560243560443515156064351515610afb565b3480156103de57600080fd5b5061035d600435610c34565b3480156103f657600080fd5b5061022a610dad565b34801561040b57600080fd5b50610143600435610db3565b34801561042357600080fd5b50610143600435610e2a565b34801561043b57600080fd5b50604080516020600460443581810135601f810184900484028501840190955284845261022a948235600160a060020a0316946024803595369594606494920191908190840183828082843750949750610ef59650505050505050565b3480156104a457600080fd5b5061022a610f14565b3480156104b957600080fd5b5061022a610f19565b3480156104ce57600080fd5b50610143600160a060020a0360043581169060243516610f1f565b3480156104f557600080fd5b50610143600435611085565b600380548290811061050f57fe5b600091825260209091200154600160a060020a0316905081565b600033301461053757600080fd5b600160a060020a038216600090815260026020526040902054829060ff16151561056057600080fd5b600160a060020a0383166000908152600260205260408120805460ff1916905591505b6003546000190182101561063b5782600160a060020a03166003838154811015156105aa57fe5b600091825260209091200154600160a060020a03161415610630576003805460001981019081106105d757fe5b60009182526020909120015460038054600160a060020a0390921691849081106105fd57fe5b9060005260206000200160006101000a815481600160a060020a030219169083600160a060020a0316021790555061063b565b600190910190610583565b60038054600019019061064e90826112c8565b5060035460045411156106675760035461066790610db3565b604051600160a060020a038416906000805160206113a583398151915290600090a2505050565b3360008181526002602052604090205460ff1615156106ac57600080fd5b60008281526001602090815260408083203380855292529091205483919060ff1615156106d857600080fd5b600084815260208190526040902060030154849060ff16156106f957600080fd5b6000858152600160209081526040808320338085529252808320805460ff191690555187927ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e991a35050505050565b60026020526000908152604090205460ff1681565b600160209081526000928352604080842090915290825290205460ff1681565b6000805b6005548110156107e2578380156107aa575060008181526020819052604090206003015460ff16155b806107ce57508280156107ce575060008181526020819052604090206003015460ff165b156107da576001820191505b600101610781565b5092915050565b3330146107f557600080fd5b600160a060020a038116600090815260026020526040902054819060ff161561081d57600080fd5b81600160a060020a038116151561083357600080fd5b600380549050600101600454603282118061084d57508181115b80610856575080155b8061085f575081155b1561086957600080fd5b600160a060020a038516600081815260026020526040808220805460ff1916600190811790915560038054918201815583527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b018054600160a060020a03191684179055516000805160206113858339815191529190a25050505050565b600080805b600354811015610964576000848152600160205260408120600380549192918490811061091557fe5b6000918252602080832090910154600160a060020a0316835282019290925260400190205460ff1615610949576001820191505b60045482141561095c5760019250610964565b6001016108ec565b5050919050565b6000805b6003548110156109d4576000838152600160205260408120600380549192918490811061099857fe5b6000918252602080832090910154600160a060020a0316835282019290925260400190205460ff16156109cc576001820191505b60010161096f565b50919050565b6000602081815291815260409081902080546001808301546002808501805487516101009582161595909502600019011691909104601f8101889004880284018801909652858352600160a060020a0390931695909491929190830182828015610a855780601f10610a5a57610100808354040283529160200191610a85565b820191906000526020600020905b815481529060010190602001808311610a6857829003601f168201915b5050506003909301549192505060ff1684565b60606003805480602002602001604051908101604052809291908181526020018280548015610af057602002820191906000526020600020905b8154600160a060020a03168152600190910190602001808311610ad2575b505050505090505b90565b606080600080600554604051908082528060200260200182016040528015610b2d578160200160208202803883390190505b50925060009150600090505b600554811015610bb457858015610b62575060008181526020819052604090206003015460ff16155b80610b865750848015610b86575060008181526020819052604090206003015460ff165b15610bac57808383815181101515610b9a57fe5b60209081029091010152600191909101905b600101610b39565b878703604051908082528060200260200182016040528015610be0578160200160208202803883390190505b5093508790505b86811015610c29578281815181101515610bfd57fe5b9060200190602002015184898303815181101515610c1757fe5b60209081029091010152600101610be7565b505050949350505050565b606080600080600380549050604051908082528060200260200182016040528015610c69578160200160208202803883390190505b50925060009150600090505b600354811015610d265760008581526001602052604081206003805491929184908110610c9e57fe5b6000918252602080832090910154600160a060020a0316835282019290925260400190205460ff1615610d1e576003805482908110610cd957fe5b6000918252602090912001548351600160a060020a0390911690849084908110610cff57fe5b600160a060020a03909216602092830290910190910152600191909101905b600101610c75565b81604051908082528060200260200182016040528015610d50578160200160208202803883390190505b509350600090505b81811015610da5578281815181101515610d6e57fe5b906020019060200201518482815181101515610d8657fe5b600160a060020a03909216602092830290910190910152600101610d58565b505050919050565b60055481565b333014610dbf57600080fd5b600354816032821180610dd157508181115b80610dda575080155b80610de3575081155b15610ded57600080fd5b60048390556040805184815290517fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a9181900360200190a1505050565b3360008181526002602052604090205460ff161515610e4857600080fd5b6000828152602081905260409020548290600160a060020a03161515610e6d57600080fd5b60008381526001602090815260408083203380855292529091205484919060ff1615610e9857600080fd5b6000858152600160208181526040808420338086529252808420805460ff1916909317909255905187927f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef91a3610eee85611085565b5050505050565b6000610f028484846111e5565b9050610f0d81610e2a565b9392505050565b603281565b60045481565b6000333014610f2d57600080fd5b600160a060020a038316600090815260026020526040902054839060ff161515610f5657600080fd5b600160a060020a038316600090815260026020526040902054839060ff1615610f7e57600080fd5b600092505b60035483101561100f5784600160a060020a0316600384815481101515610fa657fe5b600091825260209091200154600160a060020a031614156110045783600384815481101515610fd157fe5b9060005260206000200160006101000a815481600160a060020a030219169083600160a060020a0316021790555061100f565b600190920191610f83565b600160a060020a03808616600081815260026020526040808220805460ff1990811690915593881682528082208054909416600117909355915190916000805160206113a583398151915291a2604051600160a060020a0385169060008051602061138583398151915290600090a25050505050565b600081815260208190526040812060030154829060ff16156110a657600080fd5b6110af836108e7565b156111e0576000838152602081905260409081902060038101805460ff19166001908117909155815481830154935160028085018054959850600160a060020a039093169594929391928392859260001991831615610100029190910190911604801561115d5780601f106111325761010080835404028352916020019161115d565b820191906000526020600020905b81548152906001019060200180831161114057829003601f168201915b505091505060006040518083038185875af192505050156111a85760405183907f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7590600090a26111e0565b60405183907f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923690600090a260038201805460ff191690555b505050565b600083600160a060020a03811615156111fd57600080fd5b60055460408051608081018252600160a060020a03888116825260208083018981528385018981526000606086018190528781528084529590952084518154600160a060020a031916941693909317835551600183015592518051949650919390926112709260028501929101906112ec565b50606091909101516003909101805460ff191691151591909117905560058054600101905560405182907fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5190600090a2509392505050565b8154818355818111156111e0576000838152602090206111e091810190830161136a565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061132d57805160ff191683800117855561135a565b8280016001018555821561135a579182015b8281111561135a57825182559160200191906001019061133f565b5061136692915061136a565b5090565b610af891905b8082111561136657600081556001016113705600f39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b90a165627a7a7230582020d4aa1899272fabc8cfb060a37f2e45089fc888afa757aa521e5313c5a387060029", - "opcodes": - "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH3 0x11 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 MLOAD PUSH3 0x15F6 CODESIZE SUB DUP1 PUSH3 0x15F6 DUP4 CODECOPY DUP2 ADD PUSH1 0x40 MSTORE DUP1 MLOAD PUSH1 0x20 DUP3 ADD MLOAD SWAP2 ADD DUP1 MLOAD SWAP1 SWAP2 SWAP1 PUSH1 0x0 SWAP1 DUP3 PUSH1 0x32 DUP3 GT DUP1 PUSH3 0x48 JUMPI POP DUP2 DUP2 GT JUMPDEST DUP1 PUSH3 0x52 JUMPI POP DUP1 ISZERO JUMPDEST DUP1 PUSH3 0x5C JUMPI POP DUP2 ISZERO JUMPDEST ISZERO PUSH3 0x67 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 SWAP3 POP JUMPDEST DUP5 MLOAD DUP4 LT ISZERO PUSH3 0x13B JUMPI PUSH1 0x2 PUSH1 0x0 DUP7 DUP6 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH3 0x89 JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD DUP2 ADD MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP3 MSTORE DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x40 ADD PUSH1 0x0 KECCAK256 SLOAD PUSH1 0xFF AND DUP1 PUSH3 0xDC JUMPI POP DUP5 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH3 0xC5 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x0 EQ JUMPDEST ISZERO PUSH3 0xE7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0x2 PUSH1 0x0 DUP8 DUP7 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH3 0xFC JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP2 SWAP1 SWAP2 ADD DUP2 ADD MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP3 MSTORE DUP2 ADD SWAP2 SWAP1 SWAP2 MSTORE PUSH1 0x40 ADD PUSH1 0x0 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT AND SWAP2 ISZERO ISZERO SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE PUSH1 0x1 SWAP3 SWAP1 SWAP3 ADD SWAP2 PUSH3 0x6C JUMP JUMPDEST DUP5 MLOAD PUSH3 0x150 SWAP1 PUSH1 0x3 SWAP1 PUSH1 0x20 DUP9 ADD SWAP1 PUSH3 0x162 JUMP JUMPDEST POP POP POP PUSH1 0x4 SWAP2 SWAP1 SWAP2 SSTORE POP PUSH3 0x1F6 SWAP1 POP JUMP JUMPDEST DUP3 DUP1 SLOAD DUP3 DUP3 SSTORE SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 DUP2 ADD SWAP3 DUP3 ISZERO PUSH3 0x1BA JUMPI SWAP2 PUSH1 0x20 MUL DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH3 0x1BA JUMPI DUP3 MLOAD DUP3 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB NOT AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP2 AND OR DUP3 SSTORE PUSH1 0x20 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH3 0x183 JUMP JUMPDEST POP PUSH3 0x1C8 SWAP3 SWAP2 POP PUSH3 0x1CC JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH3 0x1F3 SWAP2 SWAP1 JUMPDEST DUP1 DUP3 GT ISZERO PUSH3 0x1C8 JUMPI DUP1 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB NOT AND DUP2 SSTORE PUSH1 0x1 ADD PUSH3 0x1D3 JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH2 0x13F0 DUP1 PUSH3 0x206 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x103 JUMPI PUSH4 0xFFFFFFFF PUSH1 0xE0 PUSH1 0x2 EXP PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x25E7C27 DUP2 EQ PUSH2 0x145 JUMPI DUP1 PUSH4 0x173825D9 EQ PUSH2 0x179 JUMPI DUP1 PUSH4 0x20EA8D86 EQ PUSH2 0x19A JUMPI DUP1 PUSH4 0x2F54BF6E EQ PUSH2 0x1B2 JUMPI DUP1 PUSH4 0x3411C81C EQ PUSH2 0x1E7 JUMPI DUP1 PUSH4 0x54741525 EQ PUSH2 0x20B JUMPI DUP1 PUSH4 0x7065CB48 EQ PUSH2 0x23C JUMPI DUP1 PUSH4 0x784547A7 EQ PUSH2 0x25D JUMPI DUP1 PUSH4 0x8B51D13F EQ PUSH2 0x275 JUMPI DUP1 PUSH4 0x9ACE38C2 EQ PUSH2 0x28D JUMPI DUP1 PUSH4 0xA0E67E2B EQ PUSH2 0x348 JUMPI DUP1 PUSH4 0xA8ABE69A EQ PUSH2 0x3AD JUMPI DUP1 PUSH4 0xB5DC40C3 EQ PUSH2 0x3D2 JUMPI DUP1 PUSH4 0xB77BF600 EQ PUSH2 0x3EA JUMPI DUP1 PUSH4 0xBA51A6DF EQ PUSH2 0x3FF JUMPI DUP1 PUSH4 0xC01A8C84 EQ PUSH2 0x417 JUMPI DUP1 PUSH4 0xC6427474 EQ PUSH2 0x42F JUMPI DUP1 PUSH4 0xD74F8EDD EQ PUSH2 0x498 JUMPI DUP1 PUSH4 0xDC8452CD EQ PUSH2 0x4AD JUMPI DUP1 PUSH4 0xE20056E6 EQ PUSH2 0x4C2 JUMPI DUP1 PUSH4 0xEE22610B EQ PUSH2 0x4E9 JUMPI JUMPDEST PUSH1 0x0 CALLVALUE GT ISZERO PUSH2 0x143 JUMPI PUSH1 0x40 DUP1 MLOAD CALLVALUE DUP2 MSTORE SWAP1 MLOAD CALLER SWAP2 PUSH32 0xE1FFFCC4923D04B559F4D29A8BFC6CDA04EB5B0D3C460751C2402C5C5CC9109C SWAP2 SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 LOG2 JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x151 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x15D PUSH1 0x4 CALLDATALOAD PUSH2 0x501 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x185 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x143 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB PUSH1 0x4 CALLDATALOAD AND PUSH2 0x529 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1A6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x143 PUSH1 0x4 CALLDATALOAD PUSH2 0x68E JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1BE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1D3 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB PUSH1 0x4 CALLDATALOAD AND PUSH2 0x748 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1F3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1D3 PUSH1 0x4 CALLDATALOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB PUSH1 0x24 CALLDATALOAD AND PUSH2 0x75D JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x217 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x22A PUSH1 0x4 CALLDATALOAD ISZERO ISZERO PUSH1 0x24 CALLDATALOAD ISZERO ISZERO PUSH2 0x77D JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x248 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x143 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB PUSH1 0x4 CALLDATALOAD AND PUSH2 0x7E9 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x269 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1D3 PUSH1 0x4 CALLDATALOAD PUSH2 0x8E7 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x281 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x22A PUSH1 0x4 CALLDATALOAD PUSH2 0x96B JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x299 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x2A5 PUSH1 0x4 CALLDATALOAD PUSH2 0x9DA JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP6 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP2 MSTORE PUSH1 0x20 ADD DUP5 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH1 0x20 ADD DUP4 ISZERO ISZERO ISZERO ISZERO DUP2 MSTORE PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE DUP5 DUP2 DUP2 MLOAD DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x30A JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x2F2 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x337 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP6 POP POP POP POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x354 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x35D PUSH2 0xA98 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 DUP2 ADD SWAP2 MUL DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x399 JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x381 JUMP JUMPDEST POP POP POP POP SWAP1 POP ADD SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3B9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x35D PUSH1 0x4 CALLDATALOAD PUSH1 0x24 CALLDATALOAD PUSH1 0x44 CALLDATALOAD ISZERO ISZERO PUSH1 0x64 CALLDATALOAD ISZERO ISZERO PUSH2 0xAFB JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3DE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x35D PUSH1 0x4 CALLDATALOAD PUSH2 0xC34 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3F6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x22A PUSH2 0xDAD JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x40B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x143 PUSH1 0x4 CALLDATALOAD PUSH2 0xDB3 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x423 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x143 PUSH1 0x4 CALLDATALOAD PUSH2 0xE2A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x43B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 PUSH1 0x4 PUSH1 0x44 CALLDATALOAD DUP2 DUP2 ADD CALLDATALOAD PUSH1 0x1F DUP2 ADD DUP5 SWAP1 DIV DUP5 MUL DUP6 ADD DUP5 ADD SWAP1 SWAP6 MSTORE DUP5 DUP5 MSTORE PUSH2 0x22A SWAP5 DUP3 CALLDATALOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP5 PUSH1 0x24 DUP1 CALLDATALOAD SWAP6 CALLDATASIZE SWAP6 SWAP5 PUSH1 0x64 SWAP5 SWAP3 ADD SWAP2 SWAP1 DUP2 SWAP1 DUP5 ADD DUP4 DUP3 DUP1 DUP3 DUP5 CALLDATACOPY POP SWAP5 SWAP8 POP PUSH2 0xEF5 SWAP7 POP POP POP POP POP POP POP JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4A4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x22A PUSH2 0xF14 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4B9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x22A PUSH2 0xF19 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4CE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x143 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH2 0xF1F JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4F5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x143 PUSH1 0x4 CALLDATALOAD PUSH2 0x1085 JUMP JUMPDEST PUSH1 0x3 DUP1 SLOAD DUP3 SWAP1 DUP2 LT PUSH2 0x50F JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 SWAP1 SWAP2 KECCAK256 ADD SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP1 POP DUP2 JUMP JUMPDEST PUSH1 0x0 CALLER ADDRESS EQ PUSH2 0x537 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP3 SWAP1 PUSH1 0xFF AND ISZERO ISZERO PUSH2 0x560 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT AND SWAP1 SSTORE SWAP2 POP JUMPDEST PUSH1 0x3 SLOAD PUSH1 0x0 NOT ADD DUP3 LT ISZERO PUSH2 0x63B JUMPI DUP3 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x3 DUP4 DUP2 SLOAD DUP2 LT ISZERO ISZERO PUSH2 0x5AA JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 SWAP1 SWAP2 KECCAK256 ADD SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ ISZERO PUSH2 0x630 JUMPI PUSH1 0x3 DUP1 SLOAD PUSH1 0x0 NOT DUP2 ADD SWAP1 DUP2 LT PUSH2 0x5D7 JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 SWAP1 SWAP2 KECCAK256 ADD SLOAD PUSH1 0x3 DUP1 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND SWAP2 DUP5 SWAP1 DUP2 LT PUSH2 0x5FD JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 ADD PUSH1 0x0 PUSH2 0x100 EXP DUP2 SLOAD DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB MUL NOT AND SWAP1 DUP4 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND MUL OR SWAP1 SSTORE POP PUSH2 0x63B JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH2 0x583 JUMP JUMPDEST PUSH1 0x3 DUP1 SLOAD PUSH1 0x0 NOT ADD SWAP1 PUSH2 0x64E SWAP1 DUP3 PUSH2 0x12C8 JUMP JUMPDEST POP PUSH1 0x3 SLOAD PUSH1 0x4 SLOAD GT ISZERO PUSH2 0x667 JUMPI PUSH1 0x3 SLOAD PUSH2 0x667 SWAP1 PUSH2 0xDB3 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP5 AND SWAP1 PUSH1 0x0 DUP1 MLOAD PUSH1 0x20 PUSH2 0x13A5 DUP4 CODECOPY DUP2 MLOAD SWAP2 MSTORE SWAP1 PUSH1 0x0 SWAP1 LOG2 POP POP POP JUMP JUMPDEST CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND ISZERO ISZERO PUSH2 0x6AC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP3 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP1 DUP6 MSTORE SWAP3 MSTORE SWAP1 SWAP2 KECCAK256 SLOAD DUP4 SWAP2 SWAP1 PUSH1 0xFF AND ISZERO ISZERO PUSH2 0x6D8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP5 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 PUSH1 0x3 ADD SLOAD DUP5 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0x6F9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP6 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP1 DUP6 MSTORE SWAP3 MSTORE DUP1 DUP4 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT AND SWAP1 SSTORE MLOAD DUP8 SWAP3 PUSH32 0xF6A317157440607F36269043EB55F1287A5A19BA2216AFEAB88CD46CBCFB88E9 SWAP2 LOG3 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 JUMPDEST PUSH1 0x5 SLOAD DUP2 LT ISZERO PUSH2 0x7E2 JUMPI DUP4 DUP1 ISZERO PUSH2 0x7AA JUMPI POP PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 PUSH1 0x3 ADD SLOAD PUSH1 0xFF AND ISZERO JUMPDEST DUP1 PUSH2 0x7CE JUMPI POP DUP3 DUP1 ISZERO PUSH2 0x7CE JUMPI POP PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 PUSH1 0x3 ADD SLOAD PUSH1 0xFF AND JUMPDEST ISZERO PUSH2 0x7DA JUMPI PUSH1 0x1 DUP3 ADD SWAP2 POP JUMPDEST PUSH1 0x1 ADD PUSH2 0x781 JUMP JUMPDEST POP SWAP3 SWAP2 POP POP JUMP JUMPDEST CALLER ADDRESS EQ PUSH2 0x7F5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP2 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0x81D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP2 AND ISZERO ISZERO PUSH2 0x833 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x3 DUP1 SLOAD SWAP1 POP PUSH1 0x1 ADD PUSH1 0x4 SLOAD PUSH1 0x32 DUP3 GT DUP1 PUSH2 0x84D JUMPI POP DUP2 DUP2 GT JUMPDEST DUP1 PUSH2 0x856 JUMPI POP DUP1 ISZERO JUMPDEST DUP1 PUSH2 0x85F JUMPI POP DUP2 ISZERO JUMPDEST ISZERO PUSH2 0x869 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP6 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT AND PUSH1 0x1 SWAP1 DUP2 OR SWAP1 SWAP2 SSTORE PUSH1 0x3 DUP1 SLOAD SWAP2 DUP3 ADD DUP2 SSTORE DUP4 MSTORE PUSH32 0xC2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B ADD DUP1 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB NOT AND DUP5 OR SWAP1 SSTORE MLOAD PUSH1 0x0 DUP1 MLOAD PUSH1 0x20 PUSH2 0x1385 DUP4 CODECOPY DUP2 MLOAD SWAP2 MSTORE SWAP2 SWAP1 LOG2 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP1 JUMPDEST PUSH1 0x3 SLOAD DUP2 LT ISZERO PUSH2 0x964 JUMPI PUSH1 0x0 DUP5 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 PUSH1 0x3 DUP1 SLOAD SWAP2 SWAP3 SWAP2 DUP5 SWAP1 DUP2 LT PUSH2 0x915 JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 DUP1 DUP4 KECCAK256 SWAP1 SWAP2 ADD SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP4 MSTORE DUP3 ADD SWAP3 SWAP1 SWAP3 MSTORE PUSH1 0x40 ADD SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x949 JUMPI PUSH1 0x1 DUP3 ADD SWAP2 POP JUMPDEST PUSH1 0x4 SLOAD DUP3 EQ ISZERO PUSH2 0x95C JUMPI PUSH1 0x1 SWAP3 POP PUSH2 0x964 JUMP JUMPDEST PUSH1 0x1 ADD PUSH2 0x8EC JUMP JUMPDEST POP POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 JUMPDEST PUSH1 0x3 SLOAD DUP2 LT ISZERO PUSH2 0x9D4 JUMPI PUSH1 0x0 DUP4 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 PUSH1 0x3 DUP1 SLOAD SWAP2 SWAP3 SWAP2 DUP5 SWAP1 DUP2 LT PUSH2 0x998 JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 DUP1 DUP4 KECCAK256 SWAP1 SWAP2 ADD SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP4 MSTORE DUP3 ADD SWAP3 SWAP1 SWAP3 MSTORE PUSH1 0x40 ADD SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x9CC JUMPI PUSH1 0x1 DUP3 ADD SWAP2 POP JUMPDEST PUSH1 0x1 ADD PUSH2 0x96F JUMP JUMPDEST POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP2 DUP2 MSTORE SWAP2 DUP2 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH1 0x1 DUP1 DUP4 ADD SLOAD PUSH1 0x2 DUP1 DUP6 ADD DUP1 SLOAD DUP8 MLOAD PUSH2 0x100 SWAP6 DUP3 AND ISZERO SWAP6 SWAP1 SWAP6 MUL PUSH1 0x0 NOT ADD AND SWAP2 SWAP1 SWAP2 DIV PUSH1 0x1F DUP2 ADD DUP9 SWAP1 DIV DUP9 MUL DUP5 ADD DUP9 ADD SWAP1 SWAP7 MSTORE DUP6 DUP4 MSTORE PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP4 AND SWAP6 SWAP1 SWAP5 SWAP2 SWAP3 SWAP2 SWAP1 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0xA85 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0xA5A JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0xA85 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 0xA68 JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP PUSH1 0x3 SWAP1 SWAP4 ADD SLOAD SWAP2 SWAP3 POP POP PUSH1 0xFF AND DUP5 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x3 DUP1 SLOAD DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP3 DUP1 SLOAD DUP1 ISZERO PUSH2 0xAF0 JUMPI PUSH1 0x20 MUL DUP3 ADD SWAP2 SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP2 MSTORE PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0xAD2 JUMPI JUMPDEST POP POP POP POP POP SWAP1 POP JUMPDEST SWAP1 JUMP JUMPDEST PUSH1 0x60 DUP1 PUSH1 0x0 DUP1 PUSH1 0x5 SLOAD PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0xB2D JUMPI DUP2 PUSH1 0x20 ADD PUSH1 0x20 DUP3 MUL DUP1 CODESIZE DUP4 CODECOPY ADD SWAP1 POP JUMPDEST POP SWAP3 POP PUSH1 0x0 SWAP2 POP PUSH1 0x0 SWAP1 POP JUMPDEST PUSH1 0x5 SLOAD DUP2 LT ISZERO PUSH2 0xBB4 JUMPI DUP6 DUP1 ISZERO PUSH2 0xB62 JUMPI POP PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 PUSH1 0x3 ADD SLOAD PUSH1 0xFF AND ISZERO JUMPDEST DUP1 PUSH2 0xB86 JUMPI POP DUP5 DUP1 ISZERO PUSH2 0xB86 JUMPI POP PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 PUSH1 0x3 ADD SLOAD PUSH1 0xFF AND JUMPDEST ISZERO PUSH2 0xBAC JUMPI DUP1 DUP4 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xB9A JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MSTORE PUSH1 0x1 SWAP2 SWAP1 SWAP2 ADD SWAP1 JUMPDEST PUSH1 0x1 ADD PUSH2 0xB39 JUMP JUMPDEST DUP8 DUP8 SUB PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0xBE0 JUMPI DUP2 PUSH1 0x20 ADD PUSH1 0x20 DUP3 MUL DUP1 CODESIZE DUP4 CODECOPY ADD SWAP1 POP JUMPDEST POP SWAP4 POP DUP8 SWAP1 POP JUMPDEST DUP7 DUP2 LT ISZERO PUSH2 0xC29 JUMPI DUP3 DUP2 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xBFD JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP5 DUP10 DUP4 SUB DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xC17 JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MSTORE PUSH1 0x1 ADD PUSH2 0xBE7 JUMP JUMPDEST POP POP POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x60 DUP1 PUSH1 0x0 DUP1 PUSH1 0x3 DUP1 SLOAD SWAP1 POP PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0xC69 JUMPI DUP2 PUSH1 0x20 ADD PUSH1 0x20 DUP3 MUL DUP1 CODESIZE DUP4 CODECOPY ADD SWAP1 POP JUMPDEST POP SWAP3 POP PUSH1 0x0 SWAP2 POP PUSH1 0x0 SWAP1 POP JUMPDEST PUSH1 0x3 SLOAD DUP2 LT ISZERO PUSH2 0xD26 JUMPI PUSH1 0x0 DUP6 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 PUSH1 0x3 DUP1 SLOAD SWAP2 SWAP3 SWAP2 DUP5 SWAP1 DUP2 LT PUSH2 0xC9E JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 DUP1 DUP4 KECCAK256 SWAP1 SWAP2 ADD SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP4 MSTORE DUP3 ADD SWAP3 SWAP1 SWAP3 MSTORE PUSH1 0x40 ADD SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0xD1E JUMPI PUSH1 0x3 DUP1 SLOAD DUP3 SWAP1 DUP2 LT PUSH2 0xCD9 JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 SWAP1 SWAP2 KECCAK256 ADD SLOAD DUP4 MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP2 AND SWAP1 DUP5 SWAP1 DUP5 SWAP1 DUP2 LT PUSH2 0xCFF JUMPI INVALID JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND PUSH1 0x20 SWAP3 DUP4 MUL SWAP1 SWAP2 ADD SWAP1 SWAP2 ADD MSTORE PUSH1 0x1 SWAP2 SWAP1 SWAP2 ADD SWAP1 JUMPDEST PUSH1 0x1 ADD PUSH2 0xC75 JUMP JUMPDEST DUP2 PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0xD50 JUMPI DUP2 PUSH1 0x20 ADD PUSH1 0x20 DUP3 MUL DUP1 CODESIZE DUP4 CODECOPY ADD SWAP1 POP JUMPDEST POP SWAP4 POP PUSH1 0x0 SWAP1 POP JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0xDA5 JUMPI DUP3 DUP2 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xD6E JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP5 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xD86 JUMPI INVALID JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND PUSH1 0x20 SWAP3 DUP4 MUL SWAP1 SWAP2 ADD SWAP1 SWAP2 ADD MSTORE PUSH1 0x1 ADD PUSH2 0xD58 JUMP JUMPDEST POP POP POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x5 SLOAD DUP2 JUMP JUMPDEST CALLER ADDRESS EQ PUSH2 0xDBF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x3 SLOAD DUP2 PUSH1 0x32 DUP3 GT DUP1 PUSH2 0xDD1 JUMPI POP DUP2 DUP2 GT JUMPDEST DUP1 PUSH2 0xDDA JUMPI POP DUP1 ISZERO JUMPDEST DUP1 PUSH2 0xDE3 JUMPI POP DUP2 ISZERO JUMPDEST ISZERO PUSH2 0xDED JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x4 DUP4 SWAP1 SSTORE PUSH1 0x40 DUP1 MLOAD DUP5 DUP2 MSTORE SWAP1 MLOAD PUSH32 0xA3F1EE9126A074D9326C682F561767F710E927FAA811F7A99829D49DC421797A SWAP2 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 LOG1 POP POP POP JUMP JUMPDEST CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND ISZERO ISZERO PUSH2 0xE48 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP3 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP3 SWAP1 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND ISZERO ISZERO PUSH2 0xE6D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP4 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP1 DUP6 MSTORE SWAP3 MSTORE SWAP1 SWAP2 KECCAK256 SLOAD DUP5 SWAP2 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0xE98 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP6 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 DUP2 DUP2 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 CALLER DUP1 DUP7 MSTORE SWAP3 MSTORE DUP1 DUP5 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT AND SWAP1 SWAP4 OR SWAP1 SWAP3 SSTORE SWAP1 MLOAD DUP8 SWAP3 PUSH32 0x4A504A94899432A9846E1AA406DCEB1BCFD538BB839071D49D1E5E23F5BE30EF SWAP2 LOG3 PUSH2 0xEEE DUP6 PUSH2 0x1085 JUMP JUMPDEST POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0xF02 DUP5 DUP5 DUP5 PUSH2 0x11E5 JUMP JUMPDEST SWAP1 POP PUSH2 0xF0D DUP2 PUSH2 0xE2A JUMP JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x32 DUP2 JUMP JUMPDEST PUSH1 0x4 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x0 CALLER ADDRESS EQ PUSH2 0xF2D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP4 SWAP1 PUSH1 0xFF AND ISZERO ISZERO PUSH2 0xF56 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP4 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0xF7E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 SWAP3 POP JUMPDEST PUSH1 0x3 SLOAD DUP4 LT ISZERO PUSH2 0x100F JUMPI DUP5 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x3 DUP5 DUP2 SLOAD DUP2 LT ISZERO ISZERO PUSH2 0xFA6 JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 SWAP1 SWAP2 KECCAK256 ADD SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ ISZERO PUSH2 0x1004 JUMPI DUP4 PUSH1 0x3 DUP5 DUP2 SLOAD DUP2 LT ISZERO ISZERO PUSH2 0xFD1 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 ADD PUSH1 0x0 PUSH2 0x100 EXP DUP2 SLOAD DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB MUL NOT AND SWAP1 DUP4 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND MUL OR SWAP1 SSTORE POP PUSH2 0x100F JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP3 ADD SWAP2 PUSH2 0xF83 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP1 DUP7 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT SWAP1 DUP2 AND SWAP1 SWAP2 SSTORE SWAP4 DUP9 AND DUP3 MSTORE DUP1 DUP3 KECCAK256 DUP1 SLOAD SWAP1 SWAP5 AND PUSH1 0x1 OR SWAP1 SWAP4 SSTORE SWAP2 MLOAD SWAP1 SWAP2 PUSH1 0x0 DUP1 MLOAD PUSH1 0x20 PUSH2 0x13A5 DUP4 CODECOPY DUP2 MLOAD SWAP2 MSTORE SWAP2 LOG2 PUSH1 0x40 MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP6 AND SWAP1 PUSH1 0x0 DUP1 MLOAD PUSH1 0x20 PUSH2 0x1385 DUP4 CODECOPY DUP2 MLOAD SWAP2 MSTORE SWAP1 PUSH1 0x0 SWAP1 LOG2 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP2 KECCAK256 PUSH1 0x3 ADD SLOAD DUP3 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0x10A6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x10AF DUP4 PUSH2 0x8E7 JUMP JUMPDEST ISZERO PUSH2 0x11E0 JUMPI PUSH1 0x0 DUP4 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 PUSH1 0x3 DUP2 ADD DUP1 SLOAD PUSH1 0xFF NOT AND PUSH1 0x1 SWAP1 DUP2 OR SWAP1 SWAP2 SSTORE DUP2 SLOAD DUP2 DUP4 ADD SLOAD SWAP4 MLOAD PUSH1 0x2 DUP1 DUP6 ADD DUP1 SLOAD SWAP6 SWAP9 POP PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP4 AND SWAP6 SWAP5 SWAP3 SWAP4 SWAP2 SWAP3 DUP4 SWAP3 DUP6 SWAP3 PUSH1 0x0 NOT SWAP2 DUP4 AND ISZERO PUSH2 0x100 MUL SWAP2 SWAP1 SWAP2 ADD SWAP1 SWAP2 AND DIV DUP1 ISZERO PUSH2 0x115D JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x1132 JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x115D 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 0x1140 JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP SWAP2 POP POP PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP6 DUP8 GAS CALL SWAP3 POP POP POP ISZERO PUSH2 0x11A8 JUMPI PUSH1 0x40 MLOAD DUP4 SWAP1 PUSH32 0x33E13ECB54C3076D8E8BB8C2881800A4D972B792045FFAE98FDF46DF365FED75 SWAP1 PUSH1 0x0 SWAP1 LOG2 PUSH2 0x11E0 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP4 SWAP1 PUSH32 0x526441BB6C1ABA3C9A4A6CA1D6545DA9C2333C8C48343EF398EB858D72B79236 SWAP1 PUSH1 0x0 SWAP1 LOG2 PUSH1 0x3 DUP3 ADD DUP1 SLOAD PUSH1 0xFF NOT AND SWAP1 SSTORE JUMPDEST POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP4 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP2 AND ISZERO ISZERO PUSH2 0x11FD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x5 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH1 0x80 DUP2 ADD DUP3 MSTORE PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP9 DUP2 AND DUP3 MSTORE PUSH1 0x20 DUP1 DUP4 ADD DUP10 DUP2 MSTORE DUP4 DUP6 ADD DUP10 DUP2 MSTORE PUSH1 0x0 PUSH1 0x60 DUP7 ADD DUP2 SWAP1 MSTORE DUP8 DUP2 MSTORE DUP1 DUP5 MSTORE SWAP6 SWAP1 SWAP6 KECCAK256 DUP5 MLOAD DUP2 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB NOT AND SWAP5 AND SWAP4 SWAP1 SWAP4 OR DUP4 SSTORE MLOAD PUSH1 0x1 DUP4 ADD SSTORE SWAP3 MLOAD DUP1 MLOAD SWAP5 SWAP7 POP SWAP2 SWAP4 SWAP1 SWAP3 PUSH2 0x1270 SWAP3 PUSH1 0x2 DUP6 ADD SWAP3 SWAP2 ADD SWAP1 PUSH2 0x12EC JUMP JUMPDEST POP PUSH1 0x60 SWAP2 SWAP1 SWAP2 ADD MLOAD PUSH1 0x3 SWAP1 SWAP2 ADD DUP1 SLOAD PUSH1 0xFF NOT AND SWAP2 ISZERO ISZERO SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE PUSH1 0x5 DUP1 SLOAD PUSH1 0x1 ADD SWAP1 SSTORE PUSH1 0x40 MLOAD DUP3 SWAP1 PUSH32 0xC0BA8FE4B176C1714197D43B9CC6BCF797A4A7461C5FE8D0EF6E184AE7601E51 SWAP1 PUSH1 0x0 SWAP1 LOG2 POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST DUP2 SLOAD DUP2 DUP4 SSTORE DUP2 DUP2 GT ISZERO PUSH2 0x11E0 JUMPI PUSH1 0x0 DUP4 DUP2 MSTORE PUSH1 0x20 SWAP1 KECCAK256 PUSH2 0x11E0 SWAP2 DUP2 ADD SWAP1 DUP4 ADD PUSH2 0x136A 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 PUSH2 0x132D JUMPI DUP1 MLOAD PUSH1 0xFF NOT AND DUP4 DUP1 ADD OR DUP6 SSTORE PUSH2 0x135A JUMP JUMPDEST DUP3 DUP1 ADD PUSH1 0x1 ADD DUP6 SSTORE DUP3 ISZERO PUSH2 0x135A JUMPI SWAP2 DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH2 0x135A JUMPI DUP3 MLOAD DUP3 SSTORE SWAP2 PUSH1 0x20 ADD SWAP2 SWAP1 PUSH1 0x1 ADD SWAP1 PUSH2 0x133F JUMP JUMPDEST POP PUSH2 0x1366 SWAP3 SWAP2 POP PUSH2 0x136A JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH2 0xAF8 SWAP2 SWAP1 JUMPDEST DUP1 DUP3 GT ISZERO PUSH2 0x1366 JUMPI PUSH1 0x0 DUP2 SSTORE PUSH1 0x1 ADD PUSH2 0x1370 JUMP STOP RETURN SWAP15 PUSH15 0x1EB0EDCF53C221607B54B00CD28F31 SWAP7 INVALID 0xd0 LOG2 0x49 SWAP5 0xdc ADDRESS DUP12 DUP16 PUSH2 0x1B68 0x2d DUP1 ADD SSTORE GASPRICE SWAP2 PUSH15 0xF2F495D26A907CC54D96ED840D7BDA PUSH18 0xE73194BF5A9DF7A76B90A165627A7A723058 KECCAK256 KECCAK256 0xd4 0xaa XOR SWAP10 0x27 0x2f 0xab 0xc8 0xcf 0xb0 PUSH1 0xA3 PUSH32 0x2E45089FC888AFA757AA521E5313C5A387060029000000000000000000000000 ", - "sourceMap": - "186:11249:0:-;;;2814:370;8:9:-1;5:2;;;30:1;27;20:12;5:2;2814:370:0;;;;;;;;;;;;;;;;;;;;;;2913:14;;2814:370;;;2959:6;;2814:370;256:2;2236:28;;;:66;;;2292:10;2280:9;:22;2236:66;:96;;;-1:-1:-1;2318:14:0;;2236:96;:127;;;-1:-1:-1;2348:15:0;;2236:127;2229:153;;;2377:5;;;2229:153;2966:1;2959:8;;2954:168;2971:7;:14;2969:1;:16;2954:168;;;3010:7;:19;3018:7;3026:1;3018:10;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3010:19:0;;;;;;;;;;;-1:-1:-1;3010:19:0;;;;;:38;;;3033:7;3041:1;3033:10;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3033:15:0;3047:1;3033:15;3010:38;3006:65;;;3066:5;;;3006:65;3107:4;3085:7;:19;3093:7;3101:1;3093:10;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3085:19:0;;;;;;;;;;;-1:-1:-1;3085:19:0;:26;;-1:-1:-1;;3085:26:0;;;;;;;;;;-1:-1:-1;2987:3:0;;;;;2954:168;;;3131:16;;;;:6;;:16;;;;;:::i;:::-;-1:-1:-1;;;3157:8:0;:20;;;;-1:-1:-1;186:11249:0;;-1:-1:-1;186:11249:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;186:11249:0;-1:-1:-1;;;;;186:11249:0;;;;;;;;;;;-1:-1:-1;186:11249:0;;;;;;;-1:-1:-1;186:11249:0;;;-1:-1:-1;186:11249:0;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;;186:11249:0;;;;;;;;;:::o;:::-;;;;;;;" - }, - "deployedBytecode": { - "linkReferences": {}, - "object": - "0x6080604052600436106101035763ffffffff60e060020a600035041663025e7c278114610145578063173825d91461017957806320ea8d861461019a5780632f54bf6e146101b25780633411c81c146101e7578063547415251461020b5780637065cb481461023c578063784547a71461025d5780638b51d13f146102755780639ace38c21461028d578063a0e67e2b14610348578063a8abe69a146103ad578063b5dc40c3146103d2578063b77bf600146103ea578063ba51a6df146103ff578063c01a8c8414610417578063c64274741461042f578063d74f8edd14610498578063dc8452cd146104ad578063e20056e6146104c2578063ee22610b146104e9575b60003411156101435760408051348152905133917fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c919081900360200190a25b005b34801561015157600080fd5b5061015d600435610501565b60408051600160a060020a039092168252519081900360200190f35b34801561018557600080fd5b50610143600160a060020a0360043516610529565b3480156101a657600080fd5b5061014360043561068e565b3480156101be57600080fd5b506101d3600160a060020a0360043516610748565b604080519115158252519081900360200190f35b3480156101f357600080fd5b506101d3600435600160a060020a036024351661075d565b34801561021757600080fd5b5061022a6004351515602435151561077d565b60408051918252519081900360200190f35b34801561024857600080fd5b50610143600160a060020a03600435166107e9565b34801561026957600080fd5b506101d36004356108e7565b34801561028157600080fd5b5061022a60043561096b565b34801561029957600080fd5b506102a56004356109da565b6040518085600160a060020a0316600160a060020a031681526020018481526020018060200183151515158152602001828103825284818151815260200191508051906020019080838360005b8381101561030a5781810151838201526020016102f2565b50505050905090810190601f1680156103375780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b34801561035457600080fd5b5061035d610a98565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610399578181015183820152602001610381565b505050509050019250505060405180910390f35b3480156103b957600080fd5b5061035d60043560243560443515156064351515610afb565b3480156103de57600080fd5b5061035d600435610c34565b3480156103f657600080fd5b5061022a610dad565b34801561040b57600080fd5b50610143600435610db3565b34801561042357600080fd5b50610143600435610e2a565b34801561043b57600080fd5b50604080516020600460443581810135601f810184900484028501840190955284845261022a948235600160a060020a0316946024803595369594606494920191908190840183828082843750949750610ef59650505050505050565b3480156104a457600080fd5b5061022a610f14565b3480156104b957600080fd5b5061022a610f19565b3480156104ce57600080fd5b50610143600160a060020a0360043581169060243516610f1f565b3480156104f557600080fd5b50610143600435611085565b600380548290811061050f57fe5b600091825260209091200154600160a060020a0316905081565b600033301461053757600080fd5b600160a060020a038216600090815260026020526040902054829060ff16151561056057600080fd5b600160a060020a0383166000908152600260205260408120805460ff1916905591505b6003546000190182101561063b5782600160a060020a03166003838154811015156105aa57fe5b600091825260209091200154600160a060020a03161415610630576003805460001981019081106105d757fe5b60009182526020909120015460038054600160a060020a0390921691849081106105fd57fe5b9060005260206000200160006101000a815481600160a060020a030219169083600160a060020a0316021790555061063b565b600190910190610583565b60038054600019019061064e90826112c8565b5060035460045411156106675760035461066790610db3565b604051600160a060020a038416906000805160206113a583398151915290600090a2505050565b3360008181526002602052604090205460ff1615156106ac57600080fd5b60008281526001602090815260408083203380855292529091205483919060ff1615156106d857600080fd5b600084815260208190526040902060030154849060ff16156106f957600080fd5b6000858152600160209081526040808320338085529252808320805460ff191690555187927ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e991a35050505050565b60026020526000908152604090205460ff1681565b600160209081526000928352604080842090915290825290205460ff1681565b6000805b6005548110156107e2578380156107aa575060008181526020819052604090206003015460ff16155b806107ce57508280156107ce575060008181526020819052604090206003015460ff165b156107da576001820191505b600101610781565b5092915050565b3330146107f557600080fd5b600160a060020a038116600090815260026020526040902054819060ff161561081d57600080fd5b81600160a060020a038116151561083357600080fd5b600380549050600101600454603282118061084d57508181115b80610856575080155b8061085f575081155b1561086957600080fd5b600160a060020a038516600081815260026020526040808220805460ff1916600190811790915560038054918201815583527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b018054600160a060020a03191684179055516000805160206113858339815191529190a25050505050565b600080805b600354811015610964576000848152600160205260408120600380549192918490811061091557fe5b6000918252602080832090910154600160a060020a0316835282019290925260400190205460ff1615610949576001820191505b60045482141561095c5760019250610964565b6001016108ec565b5050919050565b6000805b6003548110156109d4576000838152600160205260408120600380549192918490811061099857fe5b6000918252602080832090910154600160a060020a0316835282019290925260400190205460ff16156109cc576001820191505b60010161096f565b50919050565b6000602081815291815260409081902080546001808301546002808501805487516101009582161595909502600019011691909104601f8101889004880284018801909652858352600160a060020a0390931695909491929190830182828015610a855780601f10610a5a57610100808354040283529160200191610a85565b820191906000526020600020905b815481529060010190602001808311610a6857829003601f168201915b5050506003909301549192505060ff1684565b60606003805480602002602001604051908101604052809291908181526020018280548015610af057602002820191906000526020600020905b8154600160a060020a03168152600190910190602001808311610ad2575b505050505090505b90565b606080600080600554604051908082528060200260200182016040528015610b2d578160200160208202803883390190505b50925060009150600090505b600554811015610bb457858015610b62575060008181526020819052604090206003015460ff16155b80610b865750848015610b86575060008181526020819052604090206003015460ff165b15610bac57808383815181101515610b9a57fe5b60209081029091010152600191909101905b600101610b39565b878703604051908082528060200260200182016040528015610be0578160200160208202803883390190505b5093508790505b86811015610c29578281815181101515610bfd57fe5b9060200190602002015184898303815181101515610c1757fe5b60209081029091010152600101610be7565b505050949350505050565b606080600080600380549050604051908082528060200260200182016040528015610c69578160200160208202803883390190505b50925060009150600090505b600354811015610d265760008581526001602052604081206003805491929184908110610c9e57fe5b6000918252602080832090910154600160a060020a0316835282019290925260400190205460ff1615610d1e576003805482908110610cd957fe5b6000918252602090912001548351600160a060020a0390911690849084908110610cff57fe5b600160a060020a03909216602092830290910190910152600191909101905b600101610c75565b81604051908082528060200260200182016040528015610d50578160200160208202803883390190505b509350600090505b81811015610da5578281815181101515610d6e57fe5b906020019060200201518482815181101515610d8657fe5b600160a060020a03909216602092830290910190910152600101610d58565b505050919050565b60055481565b333014610dbf57600080fd5b600354816032821180610dd157508181115b80610dda575080155b80610de3575081155b15610ded57600080fd5b60048390556040805184815290517fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a9181900360200190a1505050565b3360008181526002602052604090205460ff161515610e4857600080fd5b6000828152602081905260409020548290600160a060020a03161515610e6d57600080fd5b60008381526001602090815260408083203380855292529091205484919060ff1615610e9857600080fd5b6000858152600160208181526040808420338086529252808420805460ff1916909317909255905187927f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef91a3610eee85611085565b5050505050565b6000610f028484846111e5565b9050610f0d81610e2a565b9392505050565b603281565b60045481565b6000333014610f2d57600080fd5b600160a060020a038316600090815260026020526040902054839060ff161515610f5657600080fd5b600160a060020a038316600090815260026020526040902054839060ff1615610f7e57600080fd5b600092505b60035483101561100f5784600160a060020a0316600384815481101515610fa657fe5b600091825260209091200154600160a060020a031614156110045783600384815481101515610fd157fe5b9060005260206000200160006101000a815481600160a060020a030219169083600160a060020a0316021790555061100f565b600190920191610f83565b600160a060020a03808616600081815260026020526040808220805460ff1990811690915593881682528082208054909416600117909355915190916000805160206113a583398151915291a2604051600160a060020a0385169060008051602061138583398151915290600090a25050505050565b600081815260208190526040812060030154829060ff16156110a657600080fd5b6110af836108e7565b156111e0576000838152602081905260409081902060038101805460ff19166001908117909155815481830154935160028085018054959850600160a060020a039093169594929391928392859260001991831615610100029190910190911604801561115d5780601f106111325761010080835404028352916020019161115d565b820191906000526020600020905b81548152906001019060200180831161114057829003601f168201915b505091505060006040518083038185875af192505050156111a85760405183907f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7590600090a26111e0565b60405183907f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923690600090a260038201805460ff191690555b505050565b600083600160a060020a03811615156111fd57600080fd5b60055460408051608081018252600160a060020a03888116825260208083018981528385018981526000606086018190528781528084529590952084518154600160a060020a031916941693909317835551600183015592518051949650919390926112709260028501929101906112ec565b50606091909101516003909101805460ff191691151591909117905560058054600101905560405182907fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5190600090a2509392505050565b8154818355818111156111e0576000838152602090206111e091810190830161136a565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061132d57805160ff191683800117855561135a565b8280016001018555821561135a579182015b8281111561135a57825182559160200191906001019061133f565b5061136692915061136a565b5090565b610af891905b8082111561136657600081556001016113705600f39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b90a165627a7a7230582020d4aa1899272fabc8cfb060a37f2e45089fc888afa757aa521e5313c5a387060029", - "opcodes": - "PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x103 JUMPI PUSH4 0xFFFFFFFF PUSH1 0xE0 PUSH1 0x2 EXP PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x25E7C27 DUP2 EQ PUSH2 0x145 JUMPI DUP1 PUSH4 0x173825D9 EQ PUSH2 0x179 JUMPI DUP1 PUSH4 0x20EA8D86 EQ PUSH2 0x19A JUMPI DUP1 PUSH4 0x2F54BF6E EQ PUSH2 0x1B2 JUMPI DUP1 PUSH4 0x3411C81C EQ PUSH2 0x1E7 JUMPI DUP1 PUSH4 0x54741525 EQ PUSH2 0x20B JUMPI DUP1 PUSH4 0x7065CB48 EQ PUSH2 0x23C JUMPI DUP1 PUSH4 0x784547A7 EQ PUSH2 0x25D JUMPI DUP1 PUSH4 0x8B51D13F EQ PUSH2 0x275 JUMPI DUP1 PUSH4 0x9ACE38C2 EQ PUSH2 0x28D JUMPI DUP1 PUSH4 0xA0E67E2B EQ PUSH2 0x348 JUMPI DUP1 PUSH4 0xA8ABE69A EQ PUSH2 0x3AD JUMPI DUP1 PUSH4 0xB5DC40C3 EQ PUSH2 0x3D2 JUMPI DUP1 PUSH4 0xB77BF600 EQ PUSH2 0x3EA JUMPI DUP1 PUSH4 0xBA51A6DF EQ PUSH2 0x3FF JUMPI DUP1 PUSH4 0xC01A8C84 EQ PUSH2 0x417 JUMPI DUP1 PUSH4 0xC6427474 EQ PUSH2 0x42F JUMPI DUP1 PUSH4 0xD74F8EDD EQ PUSH2 0x498 JUMPI DUP1 PUSH4 0xDC8452CD EQ PUSH2 0x4AD JUMPI DUP1 PUSH4 0xE20056E6 EQ PUSH2 0x4C2 JUMPI DUP1 PUSH4 0xEE22610B EQ PUSH2 0x4E9 JUMPI JUMPDEST PUSH1 0x0 CALLVALUE GT ISZERO PUSH2 0x143 JUMPI PUSH1 0x40 DUP1 MLOAD CALLVALUE DUP2 MSTORE SWAP1 MLOAD CALLER SWAP2 PUSH32 0xE1FFFCC4923D04B559F4D29A8BFC6CDA04EB5B0D3C460751C2402C5C5CC9109C SWAP2 SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 LOG2 JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x151 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x15D PUSH1 0x4 CALLDATALOAD PUSH2 0x501 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x185 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x143 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB PUSH1 0x4 CALLDATALOAD AND PUSH2 0x529 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1A6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x143 PUSH1 0x4 CALLDATALOAD PUSH2 0x68E JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1BE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1D3 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB PUSH1 0x4 CALLDATALOAD AND PUSH2 0x748 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x1F3 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1D3 PUSH1 0x4 CALLDATALOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB PUSH1 0x24 CALLDATALOAD AND PUSH2 0x75D JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x217 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x22A PUSH1 0x4 CALLDATALOAD ISZERO ISZERO PUSH1 0x24 CALLDATALOAD ISZERO ISZERO PUSH2 0x77D JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x248 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x143 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB PUSH1 0x4 CALLDATALOAD AND PUSH2 0x7E9 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x269 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x1D3 PUSH1 0x4 CALLDATALOAD PUSH2 0x8E7 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x281 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x22A PUSH1 0x4 CALLDATALOAD PUSH2 0x96B JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x299 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x2A5 PUSH1 0x4 CALLDATALOAD PUSH2 0x9DA JUMP JUMPDEST PUSH1 0x40 MLOAD DUP1 DUP6 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP2 MSTORE PUSH1 0x20 ADD DUP5 DUP2 MSTORE PUSH1 0x20 ADD DUP1 PUSH1 0x20 ADD DUP4 ISZERO ISZERO ISZERO ISZERO DUP2 MSTORE PUSH1 0x20 ADD DUP3 DUP2 SUB DUP3 MSTORE DUP5 DUP2 DUP2 MLOAD DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP DUP1 MLOAD SWAP1 PUSH1 0x20 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x30A JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x2F2 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x337 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP6 POP POP POP POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x354 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x35D PUSH2 0xA98 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 DUP2 ADD SWAP2 MUL DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0x399 JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0x381 JUMP JUMPDEST POP POP POP POP SWAP1 POP ADD SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3B9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x35D PUSH1 0x4 CALLDATALOAD PUSH1 0x24 CALLDATALOAD PUSH1 0x44 CALLDATALOAD ISZERO ISZERO PUSH1 0x64 CALLDATALOAD ISZERO ISZERO PUSH2 0xAFB JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3DE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x35D PUSH1 0x4 CALLDATALOAD PUSH2 0xC34 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x3F6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x22A PUSH2 0xDAD JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x40B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x143 PUSH1 0x4 CALLDATALOAD PUSH2 0xDB3 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x423 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x143 PUSH1 0x4 CALLDATALOAD PUSH2 0xE2A JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x43B JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 PUSH1 0x4 PUSH1 0x44 CALLDATALOAD DUP2 DUP2 ADD CALLDATALOAD PUSH1 0x1F DUP2 ADD DUP5 SWAP1 DIV DUP5 MUL DUP6 ADD DUP5 ADD SWAP1 SWAP6 MSTORE DUP5 DUP5 MSTORE PUSH2 0x22A SWAP5 DUP3 CALLDATALOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP5 PUSH1 0x24 DUP1 CALLDATALOAD SWAP6 CALLDATASIZE SWAP6 SWAP5 PUSH1 0x64 SWAP5 SWAP3 ADD SWAP2 SWAP1 DUP2 SWAP1 DUP5 ADD DUP4 DUP3 DUP1 DUP3 DUP5 CALLDATACOPY POP SWAP5 SWAP8 POP PUSH2 0xEF5 SWAP7 POP POP POP POP POP POP POP JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4A4 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x22A PUSH2 0xF14 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4B9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x22A PUSH2 0xF19 JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4CE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x143 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH2 0xF1F JUMP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x4F5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0x143 PUSH1 0x4 CALLDATALOAD PUSH2 0x1085 JUMP JUMPDEST PUSH1 0x3 DUP1 SLOAD DUP3 SWAP1 DUP2 LT PUSH2 0x50F JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 SWAP1 SWAP2 KECCAK256 ADD SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND SWAP1 POP DUP2 JUMP JUMPDEST PUSH1 0x0 CALLER ADDRESS EQ PUSH2 0x537 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP3 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP3 SWAP1 PUSH1 0xFF AND ISZERO ISZERO PUSH2 0x560 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT AND SWAP1 SSTORE SWAP2 POP JUMPDEST PUSH1 0x3 SLOAD PUSH1 0x0 NOT ADD DUP3 LT ISZERO PUSH2 0x63B JUMPI DUP3 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x3 DUP4 DUP2 SLOAD DUP2 LT ISZERO ISZERO PUSH2 0x5AA JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 SWAP1 SWAP2 KECCAK256 ADD SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ ISZERO PUSH2 0x630 JUMPI PUSH1 0x3 DUP1 SLOAD PUSH1 0x0 NOT DUP2 ADD SWAP1 DUP2 LT PUSH2 0x5D7 JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 SWAP1 SWAP2 KECCAK256 ADD SLOAD PUSH1 0x3 DUP1 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND SWAP2 DUP5 SWAP1 DUP2 LT PUSH2 0x5FD JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 ADD PUSH1 0x0 PUSH2 0x100 EXP DUP2 SLOAD DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB MUL NOT AND SWAP1 DUP4 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND MUL OR SWAP1 SSTORE POP PUSH2 0x63B JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH2 0x583 JUMP JUMPDEST PUSH1 0x3 DUP1 SLOAD PUSH1 0x0 NOT ADD SWAP1 PUSH2 0x64E SWAP1 DUP3 PUSH2 0x12C8 JUMP JUMPDEST POP PUSH1 0x3 SLOAD PUSH1 0x4 SLOAD GT ISZERO PUSH2 0x667 JUMPI PUSH1 0x3 SLOAD PUSH2 0x667 SWAP1 PUSH2 0xDB3 JUMP JUMPDEST PUSH1 0x40 MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP5 AND SWAP1 PUSH1 0x0 DUP1 MLOAD PUSH1 0x20 PUSH2 0x13A5 DUP4 CODECOPY DUP2 MLOAD SWAP2 MSTORE SWAP1 PUSH1 0x0 SWAP1 LOG2 POP POP POP JUMP JUMPDEST CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND ISZERO ISZERO PUSH2 0x6AC JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP3 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP1 DUP6 MSTORE SWAP3 MSTORE SWAP1 SWAP2 KECCAK256 SLOAD DUP4 SWAP2 SWAP1 PUSH1 0xFF AND ISZERO ISZERO PUSH2 0x6D8 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP5 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 PUSH1 0x3 ADD SLOAD DUP5 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0x6F9 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP6 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP1 DUP6 MSTORE SWAP3 MSTORE DUP1 DUP4 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT AND SWAP1 SSTORE MLOAD DUP8 SWAP3 PUSH32 0xF6A317157440607F36269043EB55F1287A5A19BA2216AFEAB88CD46CBCFB88E9 SWAP2 LOG3 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x0 SWAP3 DUP4 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 SWAP1 SWAP2 MSTORE SWAP1 DUP3 MSTORE SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND DUP2 JUMP JUMPDEST PUSH1 0x0 DUP1 JUMPDEST PUSH1 0x5 SLOAD DUP2 LT ISZERO PUSH2 0x7E2 JUMPI DUP4 DUP1 ISZERO PUSH2 0x7AA JUMPI POP PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 PUSH1 0x3 ADD SLOAD PUSH1 0xFF AND ISZERO JUMPDEST DUP1 PUSH2 0x7CE JUMPI POP DUP3 DUP1 ISZERO PUSH2 0x7CE JUMPI POP PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 PUSH1 0x3 ADD SLOAD PUSH1 0xFF AND JUMPDEST ISZERO PUSH2 0x7DA JUMPI PUSH1 0x1 DUP3 ADD SWAP2 POP JUMPDEST PUSH1 0x1 ADD PUSH2 0x781 JUMP JUMPDEST POP SWAP3 SWAP2 POP POP JUMP JUMPDEST CALLER ADDRESS EQ PUSH2 0x7F5 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP2 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0x81D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP2 AND ISZERO ISZERO PUSH2 0x833 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x3 DUP1 SLOAD SWAP1 POP PUSH1 0x1 ADD PUSH1 0x4 SLOAD PUSH1 0x32 DUP3 GT DUP1 PUSH2 0x84D JUMPI POP DUP2 DUP2 GT JUMPDEST DUP1 PUSH2 0x856 JUMPI POP DUP1 ISZERO JUMPDEST DUP1 PUSH2 0x85F JUMPI POP DUP2 ISZERO JUMPDEST ISZERO PUSH2 0x869 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP6 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT AND PUSH1 0x1 SWAP1 DUP2 OR SWAP1 SWAP2 SSTORE PUSH1 0x3 DUP1 SLOAD SWAP2 DUP3 ADD DUP2 SSTORE DUP4 MSTORE PUSH32 0xC2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B ADD DUP1 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB NOT AND DUP5 OR SWAP1 SSTORE MLOAD PUSH1 0x0 DUP1 MLOAD PUSH1 0x20 PUSH2 0x1385 DUP4 CODECOPY DUP2 MLOAD SWAP2 MSTORE SWAP2 SWAP1 LOG2 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 DUP1 JUMPDEST PUSH1 0x3 SLOAD DUP2 LT ISZERO PUSH2 0x964 JUMPI PUSH1 0x0 DUP5 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 PUSH1 0x3 DUP1 SLOAD SWAP2 SWAP3 SWAP2 DUP5 SWAP1 DUP2 LT PUSH2 0x915 JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 DUP1 DUP4 KECCAK256 SWAP1 SWAP2 ADD SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP4 MSTORE DUP3 ADD SWAP3 SWAP1 SWAP3 MSTORE PUSH1 0x40 ADD SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x949 JUMPI PUSH1 0x1 DUP3 ADD SWAP2 POP JUMPDEST PUSH1 0x4 SLOAD DUP3 EQ ISZERO PUSH2 0x95C JUMPI PUSH1 0x1 SWAP3 POP PUSH2 0x964 JUMP JUMPDEST PUSH1 0x1 ADD PUSH2 0x8EC JUMP JUMPDEST POP POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 DUP1 JUMPDEST PUSH1 0x3 SLOAD DUP2 LT ISZERO PUSH2 0x9D4 JUMPI PUSH1 0x0 DUP4 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 PUSH1 0x3 DUP1 SLOAD SWAP2 SWAP3 SWAP2 DUP5 SWAP1 DUP2 LT PUSH2 0x998 JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 DUP1 DUP4 KECCAK256 SWAP1 SWAP2 ADD SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP4 MSTORE DUP3 ADD SWAP3 SWAP1 SWAP3 MSTORE PUSH1 0x40 ADD SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0x9CC JUMPI PUSH1 0x1 DUP3 ADD SWAP2 POP JUMPDEST PUSH1 0x1 ADD PUSH2 0x96F JUMP JUMPDEST POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x0 PUSH1 0x20 DUP2 DUP2 MSTORE SWAP2 DUP2 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 DUP1 SLOAD PUSH1 0x1 DUP1 DUP4 ADD SLOAD PUSH1 0x2 DUP1 DUP6 ADD DUP1 SLOAD DUP8 MLOAD PUSH2 0x100 SWAP6 DUP3 AND ISZERO SWAP6 SWAP1 SWAP6 MUL PUSH1 0x0 NOT ADD AND SWAP2 SWAP1 SWAP2 DIV PUSH1 0x1F DUP2 ADD DUP9 SWAP1 DIV DUP9 MUL DUP5 ADD DUP9 ADD SWAP1 SWAP7 MSTORE DUP6 DUP4 MSTORE PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP4 AND SWAP6 SWAP1 SWAP5 SWAP2 SWAP3 SWAP2 SWAP1 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0xA85 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0xA5A JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0xA85 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 0xA68 JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP PUSH1 0x3 SWAP1 SWAP4 ADD SLOAD SWAP2 SWAP3 POP POP PUSH1 0xFF AND DUP5 JUMP JUMPDEST PUSH1 0x60 PUSH1 0x3 DUP1 SLOAD DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD PUSH1 0x40 MLOAD SWAP1 DUP2 ADD PUSH1 0x40 MSTORE DUP1 SWAP3 SWAP2 SWAP1 DUP2 DUP2 MSTORE PUSH1 0x20 ADD DUP3 DUP1 SLOAD DUP1 ISZERO PUSH2 0xAF0 JUMPI PUSH1 0x20 MUL DUP3 ADD SWAP2 SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP2 MSTORE PUSH1 0x1 SWAP1 SWAP2 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0xAD2 JUMPI JUMPDEST POP POP POP POP POP SWAP1 POP JUMPDEST SWAP1 JUMP JUMPDEST PUSH1 0x60 DUP1 PUSH1 0x0 DUP1 PUSH1 0x5 SLOAD PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0xB2D JUMPI DUP2 PUSH1 0x20 ADD PUSH1 0x20 DUP3 MUL DUP1 CODESIZE DUP4 CODECOPY ADD SWAP1 POP JUMPDEST POP SWAP3 POP PUSH1 0x0 SWAP2 POP PUSH1 0x0 SWAP1 POP JUMPDEST PUSH1 0x5 SLOAD DUP2 LT ISZERO PUSH2 0xBB4 JUMPI DUP6 DUP1 ISZERO PUSH2 0xB62 JUMPI POP PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 PUSH1 0x3 ADD SLOAD PUSH1 0xFF AND ISZERO JUMPDEST DUP1 PUSH2 0xB86 JUMPI POP DUP5 DUP1 ISZERO PUSH2 0xB86 JUMPI POP PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 PUSH1 0x3 ADD SLOAD PUSH1 0xFF AND JUMPDEST ISZERO PUSH2 0xBAC JUMPI DUP1 DUP4 DUP4 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xB9A JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MSTORE PUSH1 0x1 SWAP2 SWAP1 SWAP2 ADD SWAP1 JUMPDEST PUSH1 0x1 ADD PUSH2 0xB39 JUMP JUMPDEST DUP8 DUP8 SUB PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0xBE0 JUMPI DUP2 PUSH1 0x20 ADD PUSH1 0x20 DUP3 MUL DUP1 CODESIZE DUP4 CODECOPY ADD SWAP1 POP JUMPDEST POP SWAP4 POP DUP8 SWAP1 POP JUMPDEST DUP7 DUP2 LT ISZERO PUSH2 0xC29 JUMPI DUP3 DUP2 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xBFD JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP5 DUP10 DUP4 SUB DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xC17 JUMPI INVALID JUMPDEST PUSH1 0x20 SWAP1 DUP2 MUL SWAP1 SWAP2 ADD ADD MSTORE PUSH1 0x1 ADD PUSH2 0xBE7 JUMP JUMPDEST POP POP POP SWAP5 SWAP4 POP POP POP POP JUMP JUMPDEST PUSH1 0x60 DUP1 PUSH1 0x0 DUP1 PUSH1 0x3 DUP1 SLOAD SWAP1 POP PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0xC69 JUMPI DUP2 PUSH1 0x20 ADD PUSH1 0x20 DUP3 MUL DUP1 CODESIZE DUP4 CODECOPY ADD SWAP1 POP JUMPDEST POP SWAP3 POP PUSH1 0x0 SWAP2 POP PUSH1 0x0 SWAP1 POP JUMPDEST PUSH1 0x3 SLOAD DUP2 LT ISZERO PUSH2 0xD26 JUMPI PUSH1 0x0 DUP6 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 MSTORE PUSH1 0x40 DUP2 KECCAK256 PUSH1 0x3 DUP1 SLOAD SWAP2 SWAP3 SWAP2 DUP5 SWAP1 DUP2 LT PUSH2 0xC9E JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 DUP1 DUP4 KECCAK256 SWAP1 SWAP2 ADD SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND DUP4 MSTORE DUP3 ADD SWAP3 SWAP1 SWAP3 MSTORE PUSH1 0x40 ADD SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND ISZERO PUSH2 0xD1E JUMPI PUSH1 0x3 DUP1 SLOAD DUP3 SWAP1 DUP2 LT PUSH2 0xCD9 JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 SWAP1 SWAP2 KECCAK256 ADD SLOAD DUP4 MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP2 AND SWAP1 DUP5 SWAP1 DUP5 SWAP1 DUP2 LT PUSH2 0xCFF JUMPI INVALID JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND PUSH1 0x20 SWAP3 DUP4 MUL SWAP1 SWAP2 ADD SWAP1 SWAP2 ADD MSTORE PUSH1 0x1 SWAP2 SWAP1 SWAP2 ADD SWAP1 JUMPDEST PUSH1 0x1 ADD PUSH2 0xC75 JUMP JUMPDEST DUP2 PUSH1 0x40 MLOAD SWAP1 DUP1 DUP3 MSTORE DUP1 PUSH1 0x20 MUL PUSH1 0x20 ADD DUP3 ADD PUSH1 0x40 MSTORE DUP1 ISZERO PUSH2 0xD50 JUMPI DUP2 PUSH1 0x20 ADD PUSH1 0x20 DUP3 MUL DUP1 CODESIZE DUP4 CODECOPY ADD SWAP1 POP JUMPDEST POP SWAP4 POP PUSH1 0x0 SWAP1 POP JUMPDEST DUP2 DUP2 LT ISZERO PUSH2 0xDA5 JUMPI DUP3 DUP2 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xD6E JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x20 ADD SWAP1 PUSH1 0x20 MUL ADD MLOAD DUP5 DUP3 DUP2 MLOAD DUP2 LT ISZERO ISZERO PUSH2 0xD86 JUMPI INVALID JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP3 AND PUSH1 0x20 SWAP3 DUP4 MUL SWAP1 SWAP2 ADD SWAP1 SWAP2 ADD MSTORE PUSH1 0x1 ADD PUSH2 0xD58 JUMP JUMPDEST POP POP POP SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x5 SLOAD DUP2 JUMP JUMPDEST CALLER ADDRESS EQ PUSH2 0xDBF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x3 SLOAD DUP2 PUSH1 0x32 DUP3 GT DUP1 PUSH2 0xDD1 JUMPI POP DUP2 DUP2 GT JUMPDEST DUP1 PUSH2 0xDDA JUMPI POP DUP1 ISZERO JUMPDEST DUP1 PUSH2 0xDE3 JUMPI POP DUP2 ISZERO JUMPDEST ISZERO PUSH2 0xDED JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x4 DUP4 SWAP1 SSTORE PUSH1 0x40 DUP1 MLOAD DUP5 DUP2 MSTORE SWAP1 MLOAD PUSH32 0xA3F1EE9126A074D9326C682F561767F710E927FAA811F7A99829D49DC421797A SWAP2 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 LOG1 POP POP POP JUMP JUMPDEST CALLER PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD PUSH1 0xFF AND ISZERO ISZERO PUSH2 0xE48 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP3 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP3 SWAP1 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND ISZERO ISZERO PUSH2 0xE6D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP4 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 KECCAK256 CALLER DUP1 DUP6 MSTORE SWAP3 MSTORE SWAP1 SWAP2 KECCAK256 SLOAD DUP5 SWAP2 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0xE98 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 DUP6 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 DUP2 DUP2 MSTORE PUSH1 0x40 DUP1 DUP5 KECCAK256 CALLER DUP1 DUP7 MSTORE SWAP3 MSTORE DUP1 DUP5 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT AND SWAP1 SWAP4 OR SWAP1 SWAP3 SSTORE SWAP1 MLOAD DUP8 SWAP3 PUSH32 0x4A504A94899432A9846E1AA406DCEB1BCFD538BB839071D49D1E5E23F5BE30EF SWAP2 LOG3 PUSH2 0xEEE DUP6 PUSH2 0x1085 JUMP JUMPDEST POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 PUSH2 0xF02 DUP5 DUP5 DUP5 PUSH2 0x11E5 JUMP JUMPDEST SWAP1 POP PUSH2 0xF0D DUP2 PUSH2 0xE2A JUMP JUMPDEST SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x32 DUP2 JUMP JUMPDEST PUSH1 0x4 SLOAD DUP2 JUMP JUMPDEST PUSH1 0x0 CALLER ADDRESS EQ PUSH2 0xF2D JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP4 SWAP1 PUSH1 0xFF AND ISZERO ISZERO PUSH2 0xF56 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 SWAP1 KECCAK256 SLOAD DUP4 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0xF7E JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x0 SWAP3 POP JUMPDEST PUSH1 0x3 SLOAD DUP4 LT ISZERO PUSH2 0x100F JUMPI DUP5 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND PUSH1 0x3 DUP5 DUP2 SLOAD DUP2 LT ISZERO ISZERO PUSH2 0xFA6 JUMPI INVALID JUMPDEST PUSH1 0x0 SWAP2 DUP3 MSTORE PUSH1 0x20 SWAP1 SWAP2 KECCAK256 ADD SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND EQ ISZERO PUSH2 0x1004 JUMPI DUP4 PUSH1 0x3 DUP5 DUP2 SLOAD DUP2 LT ISZERO ISZERO PUSH2 0xFD1 JUMPI INVALID JUMPDEST SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 ADD PUSH1 0x0 PUSH2 0x100 EXP DUP2 SLOAD DUP2 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB MUL NOT AND SWAP1 DUP4 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB AND MUL OR SWAP1 SSTORE POP PUSH2 0x100F JUMP JUMPDEST PUSH1 0x1 SWAP1 SWAP3 ADD SWAP2 PUSH2 0xF83 JUMP JUMPDEST PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP1 DUP7 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x2 PUSH1 0x20 MSTORE PUSH1 0x40 DUP1 DUP3 KECCAK256 DUP1 SLOAD PUSH1 0xFF NOT SWAP1 DUP2 AND SWAP1 SWAP2 SSTORE SWAP4 DUP9 AND DUP3 MSTORE DUP1 DUP3 KECCAK256 DUP1 SLOAD SWAP1 SWAP5 AND PUSH1 0x1 OR SWAP1 SWAP4 SSTORE SWAP2 MLOAD SWAP1 SWAP2 PUSH1 0x0 DUP1 MLOAD PUSH1 0x20 PUSH2 0x13A5 DUP4 CODECOPY DUP2 MLOAD SWAP2 MSTORE SWAP2 LOG2 PUSH1 0x40 MLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP6 AND SWAP1 PUSH1 0x0 DUP1 MLOAD PUSH1 0x20 PUSH2 0x1385 DUP4 CODECOPY DUP2 MLOAD SWAP2 MSTORE SWAP1 PUSH1 0x0 SWAP1 LOG2 POP POP POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP2 KECCAK256 PUSH1 0x3 ADD SLOAD DUP3 SWAP1 PUSH1 0xFF AND ISZERO PUSH2 0x10A6 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH2 0x10AF DUP4 PUSH2 0x8E7 JUMP JUMPDEST ISZERO PUSH2 0x11E0 JUMPI PUSH1 0x0 DUP4 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 DUP2 SWAP1 KECCAK256 PUSH1 0x3 DUP2 ADD DUP1 SLOAD PUSH1 0xFF NOT AND PUSH1 0x1 SWAP1 DUP2 OR SWAP1 SWAP2 SSTORE DUP2 SLOAD DUP2 DUP4 ADD SLOAD SWAP4 MLOAD PUSH1 0x2 DUP1 DUP6 ADD DUP1 SLOAD SWAP6 SWAP9 POP PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB SWAP1 SWAP4 AND SWAP6 SWAP5 SWAP3 SWAP4 SWAP2 SWAP3 DUP4 SWAP3 DUP6 SWAP3 PUSH1 0x0 NOT SWAP2 DUP4 AND ISZERO PUSH2 0x100 MUL SWAP2 SWAP1 SWAP2 ADD SWAP1 SWAP2 AND DIV DUP1 ISZERO PUSH2 0x115D JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x1132 JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x115D 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 0x1140 JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP SWAP2 POP POP PUSH1 0x0 PUSH1 0x40 MLOAD DUP1 DUP4 SUB DUP2 DUP6 DUP8 GAS CALL SWAP3 POP POP POP ISZERO PUSH2 0x11A8 JUMPI PUSH1 0x40 MLOAD DUP4 SWAP1 PUSH32 0x33E13ECB54C3076D8E8BB8C2881800A4D972B792045FFAE98FDF46DF365FED75 SWAP1 PUSH1 0x0 SWAP1 LOG2 PUSH2 0x11E0 JUMP JUMPDEST PUSH1 0x40 MLOAD DUP4 SWAP1 PUSH32 0x526441BB6C1ABA3C9A4A6CA1D6545DA9C2333C8C48343EF398EB858D72B79236 SWAP1 PUSH1 0x0 SWAP1 LOG2 PUSH1 0x3 DUP3 ADD DUP1 SLOAD PUSH1 0xFF NOT AND SWAP1 SSTORE JUMPDEST POP POP POP JUMP JUMPDEST PUSH1 0x0 DUP4 PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP2 AND ISZERO ISZERO PUSH2 0x11FD JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x5 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH1 0x80 DUP2 ADD DUP3 MSTORE PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB DUP9 DUP2 AND DUP3 MSTORE PUSH1 0x20 DUP1 DUP4 ADD DUP10 DUP2 MSTORE DUP4 DUP6 ADD DUP10 DUP2 MSTORE PUSH1 0x0 PUSH1 0x60 DUP7 ADD DUP2 SWAP1 MSTORE DUP8 DUP2 MSTORE DUP1 DUP5 MSTORE SWAP6 SWAP1 SWAP6 KECCAK256 DUP5 MLOAD DUP2 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB NOT AND SWAP5 AND SWAP4 SWAP1 SWAP4 OR DUP4 SSTORE MLOAD PUSH1 0x1 DUP4 ADD SSTORE SWAP3 MLOAD DUP1 MLOAD SWAP5 SWAP7 POP SWAP2 SWAP4 SWAP1 SWAP3 PUSH2 0x1270 SWAP3 PUSH1 0x2 DUP6 ADD SWAP3 SWAP2 ADD SWAP1 PUSH2 0x12EC JUMP JUMPDEST POP PUSH1 0x60 SWAP2 SWAP1 SWAP2 ADD MLOAD PUSH1 0x3 SWAP1 SWAP2 ADD DUP1 SLOAD PUSH1 0xFF NOT AND SWAP2 ISZERO ISZERO SWAP2 SWAP1 SWAP2 OR SWAP1 SSTORE PUSH1 0x5 DUP1 SLOAD PUSH1 0x1 ADD SWAP1 SSTORE PUSH1 0x40 MLOAD DUP3 SWAP1 PUSH32 0xC0BA8FE4B176C1714197D43B9CC6BCF797A4A7461C5FE8D0EF6E184AE7601E51 SWAP1 PUSH1 0x0 SWAP1 LOG2 POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST DUP2 SLOAD DUP2 DUP4 SSTORE DUP2 DUP2 GT ISZERO PUSH2 0x11E0 JUMPI PUSH1 0x0 DUP4 DUP2 MSTORE PUSH1 0x20 SWAP1 KECCAK256 PUSH2 0x11E0 SWAP2 DUP2 ADD SWAP1 DUP4 ADD PUSH2 0x136A 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 PUSH2 0x132D JUMPI DUP1 MLOAD PUSH1 0xFF NOT AND DUP4 DUP1 ADD OR DUP6 SSTORE PUSH2 0x135A JUMP JUMPDEST DUP3 DUP1 ADD PUSH1 0x1 ADD DUP6 SSTORE DUP3 ISZERO PUSH2 0x135A JUMPI SWAP2 DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH2 0x135A JUMPI DUP3 MLOAD DUP3 SSTORE SWAP2 PUSH1 0x20 ADD SWAP2 SWAP1 PUSH1 0x1 ADD SWAP1 PUSH2 0x133F JUMP JUMPDEST POP PUSH2 0x1366 SWAP3 SWAP2 POP PUSH2 0x136A JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH2 0xAF8 SWAP2 SWAP1 JUMPDEST DUP1 DUP3 GT ISZERO PUSH2 0x1366 JUMPI PUSH1 0x0 DUP2 SSTORE PUSH1 0x1 ADD PUSH2 0x1370 JUMP STOP RETURN SWAP15 PUSH15 0x1EB0EDCF53C221607B54B00CD28F31 SWAP7 INVALID 0xd0 LOG2 0x49 SWAP5 0xdc ADDRESS DUP12 DUP16 PUSH2 0x1B68 0x2d DUP1 ADD SSTORE GASPRICE SWAP2 PUSH15 0xF2F495D26A907CC54D96ED840D7BDA PUSH18 0xE73194BF5A9DF7A76B90A165627A7A723058 KECCAK256 KECCAK256 0xd4 0xaa XOR SWAP10 0x27 0x2f 0xab 0xc8 0xcf 0xb0 PUSH1 0xA3 PUSH32 0x2E45089FC888AFA757AA521E5313C5A387060029000000000000000000000000 ", - "sourceMap": - "186:11249:0:-;;;;;;;;;-1:-1:-1;;;186:11249:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2519:1;2507:9;:13;2503:61;;;2534:30;;;2554:9;2534:30;;;;2542:10;;2534:30;;;;;;;;;;2503:61;186:11249;936:23;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;936:23:0;;;;;;;;;-1:-1:-1;;;;;936:23:0;;;;;;;;;;;;;;3711:460;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3711:460:0;-1:-1:-1;;;;;3711:460:0;;;;;6274:291;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;6274:291:0;;;;;890:40;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;890:40:0;-1:-1:-1;;;;;890:40:0;;;;;;;;;;;;;;;;;;;;;;;820:64;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;820:64:0;;;-1:-1:-1;;;;;820:64:0;;;;;9136:319;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;9136:319:0;;;;;;;;;;;;;;;;;;;;;;;;;;;3311:277;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3311:277:0;-1:-1:-1;;;;;3311:277:0;;;;;7304:337;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;7304:337:0;;;;;8622:252;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;8622:252:0;;;;;765:49;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;765:49:0;;;;;;;;;;-1:-1:-1;;;;;765:49:0;-1:-1:-1;;;;;765:49:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;765:49:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9539:115;;8:9:-1;5:2;;;30:1;27;20:12;5:2;9539:115:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;9539:115:0;;;;;;;;;;;;;;;;;10757:676;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;10757:676:0;;;;;;;;;;;;;;;9833:575;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;9833:575:0;;;;;991:28;;8:9:-1;5:2;;;30:1;27;20:12;5:2;991:28:0;;;;4990:207;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4990:207:0;;;;;5806:344;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;5806:344:0;;;;;5456:244;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;5456:244:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5456:244:0;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;5456:244:0;;-1:-1:-1;5456:244:0;;-1:-1:-1;;;;;;;5456:244:0;217:41;;8:9:-1;5:2;;;30:1;27;20:12;5:2;217:41:0;;;;965:20;;8:9:-1;5:2;;;30:1;27;20:12;5:2;965:20:0;;;;4370:449;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4370:449:0;-1:-1:-1;;;;;4370:449:0;;;;;;;;;;6679:474;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;6679:474:0;;;;;936:23;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;936:23:0;;-1:-1:-1;936:23:0;:::o;3711:460::-;3859:6;1186:10;1208:4;1186:27;1182:50;;1227:5;;;1182:50;-1:-1:-1;;;;;1420:14:0;;;;;;:7;:14;;;;;;3801:5;;1420:14;;1419:15;1415:38;;;1448:5;;;1415:38;-1:-1:-1;;;;;3822:14:0;;3839:5;3822:14;;;:7;:14;;;;;:22;;-1:-1:-1;;3822:22:0;;;3839:5;-1:-1:-1;3854:170:0;3871:6;:13;-1:-1:-1;;3871:17:0;3869:19;;3854:170;;;3924:5;-1:-1:-1;;;;;3911:18:0;:6;3918:1;3911:9;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;3911:9:0;:18;3907:117;;;3961:6;3968:13;;-1:-1:-1;;3968:17:0;;;3961:25;;;;;;;;;;;;;;;;3949:6;:9;;-1:-1:-1;;;;;3961:25:0;;;;3956:1;;3949:9;;;;;;;;;;;;;;:37;;;;;-1:-1:-1;;;;;3949:37:0;;;;;-1:-1:-1;;;;;3949:37:0;;;;;;4004:5;;3907:117;3890:3;;;;;3854:170;;;4033:6;:18;;-1:-1:-1;;4033:18:0;;;;;;:::i;:::-;-1:-1:-1;4076:6:0;:13;4065:8;;:24;4061:74;;;4121:6;:13;4103:32;;:17;:32::i;:::-;4145:19;;-1:-1:-1;;;;;4145:19:0;;;-1:-1:-1;;;;;;;;;;;4145:19:0;;;;1242:1;3711:460;;:::o;6274:291::-;6357:10;1420:14;;;;:7;:14;;;;;;;;1419:15;1415:38;;;1448:5;;;1415:38;1694:28;;;;:13;:28;;;;;;;;6402:10;1694:35;;;;;;;;;6387:13;;6402:10;1694:35;;1693:36;1689:59;;;1743:5;;;1689:59;1976:12;:27;;;;;;;;;;:36;;;6434:13;;1976:36;;1972:59;;;2026:5;;;1972:59;6506:5;6463:28;;;:13;:28;;;;;;;;6492:10;6463:40;;;;;;;;:48;;-1:-1:-1;;6463:48:0;;;6521:37;6477:13;;6521:37;;;1758:1;1463;;6274:291;;:::o;890:40::-;;;;;;;;;;;;;;;:::o;820:64::-;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;9136:319::-;9243:10;;9269:179;9286:16;;9284:1;:18;9269:179;;;9328:7;:36;;;;-1:-1:-1;9340:12:0;:15;;;;;;;;;;:24;;;;;9339:25;9328:36;:92;;;;9384:8;:36;;;;-1:-1:-1;9396:12:0;:15;;;;;;;;;;:24;;;;;9384:36;9321:127;;;9447:1;9438:10;;;;9321:127;9304:3;;9269:179;;;9136:319;;;;;:::o;3311:277::-;1186:10;1208:4;1186:27;1182:50;;1227:5;;;1182:50;-1:-1:-1;;;;;1312:14:0;;;;;;:7;:14;;;;;;3404:5;;1312:14;;1308:37;;;1340:5;;;1308:37;3427:5;-1:-1:-1;;;;;2104:13:0;;;2100:36;;;2131:5;;;2100:36;3459:6;:13;;;;3475:1;3459:17;3478:8;;256:2;2236:10;:28;:66;;;;2292:10;2280:9;:22;2236:66;:96;;;-1:-1:-1;2318:14:0;;2236:96;:127;;;-1:-1:-1;2348:15:0;;2236:127;2229:153;;;2377:5;;;2229:153;-1:-1:-1;;;;;3502:14:0;;;;;;:7;:14;;;;;;:21;;-1:-1:-1;;3502:21:0;3519:4;3502:21;;;;;;3533:6;27:10:-1;;23:18;;;45:23;;3533:18:0;;;;;;-1:-1:-1;;;;;;3533:18:0;;;;;3561:20;-1:-1:-1;;;;;;;;;;;3561:20:0;3502:14;3561:20;2146:1;;1355;1242;3311:277;:::o;7304:337::-;7394:4;;;7438:197;7455:6;:13;7453:15;;7438:197;;;7493:28;;;;:13;:28;;;;;7522:6;:9;;7493:28;;;7529:1;;7522:9;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;7522:9:0;7493:39;;;;;;;;;;;;;;;7489:71;;;7559:1;7550:10;;;;7489:71;7587:8;;7578:5;:17;7574:50;;;7620:4;7613:11;;;;7574:50;7470:3;;7438:197;;;7304:337;;;;;:::o;8622:252::-;8721:10;;8747:120;8764:6;:13;8762:15;;8747:120;;;8800:28;;;;:13;:28;;;;;8829:6;:9;;8800:28;;;8836:1;;8829:9;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;8829:9:0;8800:39;;;;;;;;;;;;;;;8796:71;;;8866:1;8857:10;;;;8796:71;8779:3;;8747:120;;;8622:252;;;;:::o;765:49::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;765:49:0;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;765:49:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;765:49:0;;;;;;;-1:-1:-1;;765:49:0;;;:::o;9539:115::-;9609:9;9641:6;9634:13;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;9634:13:0;;;;;;;;;;;;;;;;;;;;;;;9539:115;;:::o;10757:676::-;10882:22;10920:32;10993:10;11017:6;10966:16;;10955:28;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;10955:28:0;;10920:63;;11006:1;10993:14;;11040:1;11038:3;;11033:250;11045:16;;11043:1;:18;11033:250;;;11087:7;:36;;;;-1:-1:-1;11099:12:0;:15;;;;;;;;;;:24;;;;;11098:25;11087:36;:92;;;;11143:8;:36;;;;-1:-1:-1;11155:12:0;:15;;;;;;;;;;:24;;;;;11143:36;11080:203;;;11239:1;11211:18;11230:5;11211:25;;;;;;;;;;;;;;;;;;:29;11267:1;11258:10;;;;;11080:203;11063:3;;11033:250;;;11326:4;11321:2;:9;11310:21;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;11310:21:0;;11292:39;;11348:4;11346:6;;11341:85;11356:2;11354:1;:4;11341:85;;;11405:18;11424:1;11405:21;;;;;;;;;;;;;;;;;;11377:15;11397:4;11393:1;:8;11377:25;;;;;;;;;;;;;;;;;;:49;11360:3;;11341:85;;;10757:676;;;;;;;;;:::o;9833:575::-;9928:24;9968:34;10043:10;10067:6;10019;:13;;;;10005:28;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;10005:28:0;;9968:65;;10056:1;10043:14;;10090:1;10088:3;;10083:186;10095:6;:13;10093:15;;10083:186;;;10131:28;;;;:13;:28;;;;;10160:6;:9;;10131:28;;;10167:1;;10160:9;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;10160:9:0;10131:39;;;;;;;;;;;;;;;10127:142;;;10217:6;:9;;10224:1;;10217:9;;;;;;;;;;;;;;;;10190:24;;-1:-1:-1;;;;;10217:9:0;;;;10190:17;;10208:5;;10190:24;;;;;;-1:-1:-1;;;;;10190:36:0;;;:24;;;;;;;;;;:36;10253:1;10244:10;;;;;10127:142;10110:3;;10083:186;;;10309:5;10295:20;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;10295:20:0;;10278:37;;10332:1;10330:3;;10325:76;10337:5;10335:1;:7;10325:76;;;10381:17;10399:1;10381:20;;;;;;;;;;;;;;;;;;10361:14;10376:1;10361:17;;;;;;;;;;-1:-1:-1;;;;;10361:40:0;;;:17;;;;;;;;;;:40;10344:3;;10325:76;;;9833:575;;;;;;:::o;991:28::-;;;;:::o;4990:207::-;1186:10;1208:4;1186:27;1182:50;;1227:5;;;1182:50;5092:6;:13;5107:9;256:2;2236:28;;;:66;;;2292:10;2280:9;:22;2236:66;:96;;;-1:-1:-1;2318:14:0;;2236:96;:127;;;-1:-1:-1;2348:15:0;;2236:127;2229:153;;;2377:5;;;2229:153;5132:8;:20;;;5162:28;;;;;;;;;;;;;;;;;1242:1;;4990:207;:::o;5806:344::-;5889:10;1420:14;;;;:7;:14;;;;;;;;1419:15;1415:38;;;1448:5;;;1415:38;1538:12;:27;;;;;;;;;;:39;5927:13;;-1:-1:-1;;;;;1538:39:0;:44;1534:67;;;1596:5;;;1534:67;1843:28;;;;:13;:28;;;;;;;;5978:10;1843:35;;;;;;;;;5963:13;;5978:10;1843:35;;1839:58;;;1892:5;;;1839:58;6004:28;;;;6047:4;6004:28;;;;;;;;6033:10;6004:40;;;;;;;;:47;;-1:-1:-1;;6004:47:0;;;;;;;6061:39;;6018:13;;6061:39;;;6110:33;6129:13;6110:18;:33::i;:::-;1611:1;;1463;5806:344;;:::o;5456:244::-;5560:18;5610:40;5625:11;5638:5;5645:4;5610:14;:40::i;:::-;5594:56;;5660:33;5679:13;5660:18;:33::i;:::-;5456:244;;;;;:::o;217:41::-;256:2;217:41;:::o;965:20::-;;;;:::o;4370:449::-;4541:6;1186:10;1208:4;1186:27;1182:50;;1227:5;;;1182:50;-1:-1:-1;;;;;1420:14:0;;;;;;:7;:14;;;;;;4479:5;;1420:14;;1419:15;1415:38;;;1448:5;;;1415:38;-1:-1:-1;;;;;1312:14:0;;;;;;:7;:14;;;;;;4512:8;;1312:14;;1308:37;;;1340:5;;;1308:37;4548:1;4541:8;;4536:149;4553:6;:13;4551:15;;4536:149;;;4602:5;-1:-1:-1;;;;;4589:18:0;:6;4596:1;4589:9;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;4589:9:0;:18;4585:100;;;4639:8;4627:6;4634:1;4627:9;;;;;;;;;;;;;;;;;;:20;;;;;-1:-1:-1;;;;;4627:20:0;;;;;-1:-1:-1;;;;;4627:20:0;;;;;;4665:5;;4585:100;4568:3;;;;;4536:149;;;-1:-1:-1;;;;;4694:14:0;;;4711:5;4694:14;;;:7;:14;;;;;;:22;;-1:-1:-1;;4694:22:0;;;;;;4726:17;;;;;;;;:24;;;;;4694:22;4726:24;;;;4760:19;;4694:14;;-1:-1:-1;;;;;;;;;;;4760:19:0;;4789:23;;-1:-1:-1;;;;;4789:23:0;;;-1:-1:-1;;;;;;;;;;;4789:23:0;;;;1463:1;1242;4370:449;;;:::o;6679:474::-;6837:14;1976:27;;;;;;;;;;:36;;;6762:13;;1976:36;;1972:59;;;2026:5;;;1972:59;6795:26;6807:13;6795:11;:26::i;:::-;6791:356;;;6854:12;:27;;;;;;;;;;;;6895:11;;;:18;;-1:-1:-1;;6895:18:0;6909:4;6895:18;;;;;;6931:14;;6957:8;;;;6931:44;;6967:7;;;;6931:44;;6854:27;;-1:-1:-1;;;;;;6931:14:0;;;;6957:8;6967:7;;6931:44;;;;6967:7;;-1:-1:-1;;6931:44:0;;;;6895:18;6931:44;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6927:210;;;6993:24;;7003:13;;6993:24;;;;;6927:210;;;7054:31;;7071:13;;7054:31;;;;;7103:11;;;:19;;-1:-1:-1;;7103:19:0;;;6927:210;6679:474;;;:::o;7974:451::-;8106:18;8076:11;-1:-1:-1;;;;;2104:13:0;;;2100:36;;;2131:5;;;2100:36;8156:16;;8212:140;;;;;;;;-1:-1:-1;;;;;8212:140:0;;;;;;;;;;;;;;;;;;-1:-1:-1;8212:140:0;;;;;;8182:27;;;;;;;;;;:170;;;;-1:-1:-1;;;;;;8182:170:0;;;;;;;;;;-1:-1:-1;8182:170:0;;;;;;;8156:16;;-1:-1:-1;8212:140:0;;8182:27;;:170;;;;;;;;;;:::i;:::-;-1:-1:-1;8182:170:0;;;;;;;;;;;;-1:-1:-1;;8182:170:0;;;;;;;;;;8362:16;:21;;-1:-1:-1;8362:21:0;;;8393:25;;8404:13;;8393:25;;-1:-1:-1;;8393:25:0;7974:451;;;;;;:::o;186:11249::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;186:11249:0;;;-1:-1:-1;186:11249:0;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;" - } - } - }, - "sources": { - "current/multisig/MultiSigWallet/MultiSigWallet.sol": { - "id": 0 - } - }, - "sourceCodes": { - "current/multisig/MultiSigWallet/MultiSigWallet.sol": - "pragma solidity ^0.4.10;\n\n/// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution.\n/// @author Stefan George - \ncontract MultiSigWallet {\n\n uint constant public MAX_OWNER_COUNT = 50;\n\n event Confirmation(address indexed sender, uint indexed transactionId);\n event Revocation(address indexed sender, uint indexed transactionId);\n event Submission(uint indexed transactionId);\n event Execution(uint indexed transactionId);\n event ExecutionFailure(uint indexed transactionId);\n event Deposit(address indexed sender, uint value);\n event OwnerAddition(address indexed owner);\n event OwnerRemoval(address indexed owner);\n event RequirementChange(uint required);\n\n mapping (uint => Transaction) public transactions;\n mapping (uint => mapping (address => bool)) public confirmations;\n mapping (address => bool) public isOwner;\n address[] public owners;\n uint public required;\n uint public transactionCount;\n\n struct Transaction {\n address destination;\n uint value;\n bytes data;\n bool executed;\n }\n\n modifier onlyWallet() {\n if (msg.sender != address(this))\n throw;\n _;\n }\n\n modifier ownerDoesNotExist(address owner) {\n if (isOwner[owner])\n throw;\n _;\n }\n\n modifier ownerExists(address owner) {\n if (!isOwner[owner])\n throw;\n _;\n }\n\n modifier transactionExists(uint transactionId) {\n if (transactions[transactionId].destination == 0)\n throw;\n _;\n }\n\n modifier confirmed(uint transactionId, address owner) {\n if (!confirmations[transactionId][owner])\n throw;\n _;\n }\n\n modifier notConfirmed(uint transactionId, address owner) {\n if (confirmations[transactionId][owner])\n throw;\n _;\n }\n\n modifier notExecuted(uint transactionId) {\n if (transactions[transactionId].executed)\n throw;\n _;\n }\n\n modifier notNull(address _address) {\n if (_address == 0)\n throw;\n _;\n }\n\n modifier validRequirement(uint ownerCount, uint _required) {\n if ( ownerCount > MAX_OWNER_COUNT\n || _required > ownerCount\n || _required == 0\n || ownerCount == 0)\n throw;\n _;\n }\n\n /// @dev Fallback function allows to deposit ether.\n function()\n payable\n {\n if (msg.value > 0)\n Deposit(msg.sender, msg.value);\n }\n\n /*\n * Public functions\n */\n /// @dev Contract constructor sets initial owners and required number of confirmations.\n /// @param _owners List of initial owners.\n /// @param _required Number of required confirmations.\n function MultiSigWallet(address[] _owners, uint _required)\n public\n validRequirement(_owners.length, _required)\n {\n for (uint i=0; i<_owners.length; i++) {\n if (isOwner[_owners[i]] || _owners[i] == 0)\n throw;\n isOwner[_owners[i]] = true;\n }\n owners = _owners;\n required = _required;\n }\n\n /// @dev Allows to add a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of new owner.\n function addOwner(address owner)\n public\n onlyWallet\n ownerDoesNotExist(owner)\n notNull(owner)\n validRequirement(owners.length + 1, required)\n {\n isOwner[owner] = true;\n owners.push(owner);\n OwnerAddition(owner);\n }\n\n /// @dev Allows to remove an owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner.\n function removeOwner(address owner)\n public\n onlyWallet\n ownerExists(owner)\n {\n isOwner[owner] = false;\n for (uint i=0; i owners.length)\n changeRequirement(owners.length);\n OwnerRemoval(owner);\n }\n\n /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner to be replaced.\n /// @param owner Address of new owner.\n function replaceOwner(address owner, address newOwner)\n public\n onlyWallet\n ownerExists(owner)\n ownerDoesNotExist(newOwner)\n {\n for (uint i=0; i\ncontract MultiSigWallet {\n\n uint constant public MAX_OWNER_COUNT = 50;\n\n event Confirmation(address indexed sender, uint indexed transactionId);\n event Revocation(address indexed sender, uint indexed transactionId);\n event Submission(uint indexed transactionId);\n event Execution(uint indexed transactionId);\n event ExecutionFailure(uint indexed transactionId);\n event Deposit(address indexed sender, uint value);\n event OwnerAddition(address indexed owner);\n event OwnerRemoval(address indexed owner);\n event RequirementChange(uint required);\n\n mapping (uint => Transaction) public transactions;\n mapping (uint => mapping (address => bool)) public confirmations;\n mapping (address => bool) public isOwner;\n address[] public owners;\n uint public required;\n uint public transactionCount;\n\n struct Transaction {\n address destination;\n uint value;\n bytes data;\n bool executed;\n }\n\n modifier onlyWallet() {\n if (msg.sender != address(this))\n throw;\n _;\n }\n\n modifier ownerDoesNotExist(address owner) {\n if (isOwner[owner])\n throw;\n _;\n }\n\n modifier ownerExists(address owner) {\n if (!isOwner[owner])\n throw;\n _;\n }\n\n modifier transactionExists(uint transactionId) {\n if (transactions[transactionId].destination == 0)\n throw;\n _;\n }\n\n modifier confirmed(uint transactionId, address owner) {\n if (!confirmations[transactionId][owner])\n throw;\n _;\n }\n\n modifier notConfirmed(uint transactionId, address owner) {\n if (confirmations[transactionId][owner])\n throw;\n _;\n }\n\n modifier notExecuted(uint transactionId) {\n if (transactions[transactionId].executed)\n throw;\n _;\n }\n\n modifier notNull(address _address) {\n if (_address == 0)\n throw;\n _;\n }\n\n modifier validRequirement(uint ownerCount, uint _required) {\n if ( ownerCount > MAX_OWNER_COUNT\n || _required > ownerCount\n || _required == 0\n || ownerCount == 0)\n throw;\n _;\n }\n\n /// @dev Fallback function allows to deposit ether.\n function()\n payable\n {\n if (msg.value > 0)\n Deposit(msg.sender, msg.value);\n }\n\n /*\n * Public functions\n */\n /// @dev Contract constructor sets initial owners and required number of confirmations.\n /// @param _owners List of initial owners.\n /// @param _required Number of required confirmations.\n function MultiSigWallet(address[] _owners, uint _required)\n public\n validRequirement(_owners.length, _required)\n {\n for (uint i=0; i<_owners.length; i++) {\n if (isOwner[_owners[i]] || _owners[i] == 0)\n throw;\n isOwner[_owners[i]] = true;\n }\n owners = _owners;\n required = _required;\n }\n\n /// @dev Allows to add a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of new owner.\n function addOwner(address owner)\n public\n onlyWallet\n ownerDoesNotExist(owner)\n notNull(owner)\n validRequirement(owners.length + 1, required)\n {\n isOwner[owner] = true;\n owners.push(owner);\n OwnerAddition(owner);\n }\n\n /// @dev Allows to remove an owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner.\n function removeOwner(address owner)\n public\n onlyWallet\n ownerExists(owner)\n {\n isOwner[owner] = false;\n for (uint i=0; i owners.length)\n changeRequirement(owners.length);\n OwnerRemoval(owner);\n }\n\n /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner to be replaced.\n /// @param owner Address of new owner.\n function replaceOwner(address owner, address newOwner)\n public\n onlyWallet\n ownerExists(owner)\n ownerDoesNotExist(newOwner)\n {\n for (uint i=0; i\ncontract MultiSigWallet {\n\n uint constant public MAX_OWNER_COUNT = 50;\n\n event Confirmation(address indexed sender, uint indexed transactionId);\n event Revocation(address indexed sender, uint indexed transactionId);\n event Submission(uint indexed transactionId);\n event Execution(uint indexed transactionId);\n event ExecutionFailure(uint indexed transactionId);\n event Deposit(address indexed sender, uint value);\n event OwnerAddition(address indexed owner);\n event OwnerRemoval(address indexed owner);\n event RequirementChange(uint required);\n\n mapping (uint => Transaction) public transactions;\n mapping (uint => mapping (address => bool)) public confirmations;\n mapping (address => bool) public isOwner;\n address[] public owners;\n uint public required;\n uint public transactionCount;\n\n struct Transaction {\n address destination;\n uint value;\n bytes data;\n bool executed;\n }\n\n modifier onlyWallet() {\n if (msg.sender != address(this))\n throw;\n _;\n }\n\n modifier ownerDoesNotExist(address owner) {\n if (isOwner[owner])\n throw;\n _;\n }\n\n modifier ownerExists(address owner) {\n if (!isOwner[owner])\n throw;\n _;\n }\n\n modifier transactionExists(uint transactionId) {\n if (transactions[transactionId].destination == 0)\n throw;\n _;\n }\n\n modifier confirmed(uint transactionId, address owner) {\n if (!confirmations[transactionId][owner])\n throw;\n _;\n }\n\n modifier notConfirmed(uint transactionId, address owner) {\n if (confirmations[transactionId][owner])\n throw;\n _;\n }\n\n modifier notExecuted(uint transactionId) {\n if (transactions[transactionId].executed)\n throw;\n _;\n }\n\n modifier notNull(address _address) {\n if (_address == 0)\n throw;\n _;\n }\n\n modifier validRequirement(uint ownerCount, uint _required) {\n if ( ownerCount > MAX_OWNER_COUNT\n || _required > ownerCount\n || _required == 0\n || ownerCount == 0)\n throw;\n _;\n }\n\n /// @dev Fallback function allows to deposit ether.\n function()\n payable\n {\n if (msg.value > 0)\n Deposit(msg.sender, msg.value);\n }\n\n /*\n * Public functions\n */\n /// @dev Contract constructor sets initial owners and required number of confirmations.\n /// @param _owners List of initial owners.\n /// @param _required Number of required confirmations.\n function MultiSigWallet(address[] _owners, uint _required)\n public\n validRequirement(_owners.length, _required)\n {\n for (uint i=0; i<_owners.length; i++) {\n if (isOwner[_owners[i]] || _owners[i] == 0)\n throw;\n isOwner[_owners[i]] = true;\n }\n owners = _owners;\n required = _required;\n }\n\n /// @dev Allows to add a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of new owner.\n function addOwner(address owner)\n public\n onlyWallet\n ownerDoesNotExist(owner)\n notNull(owner)\n validRequirement(owners.length + 1, required)\n {\n isOwner[owner] = true;\n owners.push(owner);\n OwnerAddition(owner);\n }\n\n /// @dev Allows to remove an owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner.\n function removeOwner(address owner)\n public\n onlyWallet\n ownerExists(owner)\n {\n isOwner[owner] = false;\n for (uint i=0; i owners.length)\n changeRequirement(owners.length);\n OwnerRemoval(owner);\n }\n\n /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner to be replaced.\n /// @param owner Address of new owner.\n function replaceOwner(address owner, address newOwner)\n public\n onlyWallet\n ownerExists(owner)\n ownerDoesNotExist(newOwner)\n {\n for (uint i=0; i\ncontract MultiSigWalletWithTimeLock is MultiSigWallet {\n\n event ConfirmationTimeSet(uint indexed transactionId, uint confirmationTime);\n event TimeLockChange(uint secondsTimeLocked);\n\n uint public secondsTimeLocked;\n\n mapping (uint => uint) public confirmationTimes;\n\n modifier notFullyConfirmed(uint transactionId) {\n require(!isConfirmed(transactionId));\n _;\n }\n\n modifier fullyConfirmed(uint transactionId) {\n require(isConfirmed(transactionId));\n _;\n }\n\n modifier pastTimeLock(uint transactionId) {\n require(block.timestamp >= confirmationTimes[transactionId] + secondsTimeLocked);\n _;\n }\n\n /*\n * Public functions\n */\n\n /// @dev Contract constructor sets initial owners, required number of confirmations, and time lock.\n /// @param _owners List of initial owners.\n /// @param _required Number of required confirmations.\n /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.\n function MultiSigWalletWithTimeLock(address[] _owners, uint _required, uint _secondsTimeLocked)\n public\n MultiSigWallet(_owners, _required)\n {\n secondsTimeLocked = _secondsTimeLocked;\n }\n\n /// @dev Changes the duration of the time lock for transactions.\n /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.\n function changeTimeLock(uint _secondsTimeLocked)\n public\n onlyWallet\n {\n secondsTimeLocked = _secondsTimeLocked;\n TimeLockChange(_secondsTimeLocked);\n }\n\n /// @dev Allows an owner to confirm a transaction.\n /// @param transactionId Transaction ID.\n function confirmTransaction(uint transactionId)\n public\n ownerExists(msg.sender)\n transactionExists(transactionId)\n notConfirmed(transactionId, msg.sender)\n notFullyConfirmed(transactionId)\n {\n confirmations[transactionId][msg.sender] = true;\n Confirmation(msg.sender, transactionId);\n if (isConfirmed(transactionId)) {\n setConfirmationTime(transactionId, block.timestamp);\n }\n }\n\n /// @dev Allows an owner to revoke a confirmation for a transaction.\n /// @param transactionId Transaction ID.\n function revokeConfirmation(uint transactionId)\n public\n ownerExists(msg.sender)\n confirmed(transactionId, msg.sender)\n notExecuted(transactionId)\n notFullyConfirmed(transactionId)\n {\n confirmations[transactionId][msg.sender] = false;\n Revocation(msg.sender, transactionId);\n }\n\n /// @dev Allows anyone to execute a confirmed transaction.\n /// @param transactionId Transaction ID.\n function executeTransaction(uint transactionId)\n public\n notExecuted(transactionId)\n fullyConfirmed(transactionId)\n pastTimeLock(transactionId)\n {\n Transaction storage tx = transactions[transactionId];\n tx.executed = true;\n if (tx.destination.call.value(tx.value)(tx.data))\n Execution(transactionId);\n else {\n ExecutionFailure(transactionId);\n tx.executed = false;\n }\n }\n\n /*\n * Internal functions\n */\n\n /// @dev Sets the time of when a submission first passed.\n function setConfirmationTime(uint transactionId, uint confirmationTime)\n internal\n {\n confirmationTimes[transactionId] = confirmationTime;\n ConfirmationTimeSet(transactionId, confirmationTime);\n }\n}\n", - "previous/MultiSigWalletWithTImeLockExceptRemoveAuthorizedAddress/MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.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.10;\n\nimport \"../../current/multisig/MultiSigWalletWithTimeLock.sol\";\n\ncontract MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress is MultiSigWalletWithTimeLock {\n\n address public TOKEN_TRANSFER_PROXY_CONTRACT;\n\n modifier validRemoveAuthorizedAddressTx(uint transactionId) {\n Transaction storage tx = transactions[transactionId];\n require(tx.destination == TOKEN_TRANSFER_PROXY_CONTRACT);\n require(isFunctionRemoveAuthorizedAddress(tx.data));\n _;\n }\n\n /// @dev Contract constructor sets initial owners, required number of confirmations, time lock, and tokenTransferProxy address.\n /// @param _owners List of initial owners.\n /// @param _required Number of required confirmations.\n /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.\n /// @param _tokenTransferProxy Address of TokenTransferProxy contract.\n function MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress(\n address[] _owners,\n uint _required,\n uint _secondsTimeLocked,\n address _tokenTransferProxy)\n public\n MultiSigWalletWithTimeLock(_owners, _required, _secondsTimeLocked)\n {\n TOKEN_TRANSFER_PROXY_CONTRACT = _tokenTransferProxy;\n }\n\n /// @dev Allows execution of removeAuthorizedAddress without time lock.\n /// @param transactionId Transaction ID.\n function executeRemoveAuthorizedAddress(uint transactionId)\n public\n notExecuted(transactionId)\n fullyConfirmed(transactionId)\n validRemoveAuthorizedAddressTx(transactionId)\n {\n Transaction storage tx = transactions[transactionId];\n tx.executed = true;\n if (tx.destination.call.value(tx.value)(tx.data))\n Execution(transactionId);\n else {\n ExecutionFailure(transactionId);\n tx.executed = false;\n }\n }\n\n /// @dev Compares first 4 bytes of byte array to removeAuthorizedAddress function signature.\n /// @param data Transaction data.\n /// @return Successful if data is a call to removeAuthorizedAddress.\n function isFunctionRemoveAuthorizedAddress(bytes data)\n public\n constant\n returns (bool)\n {\n bytes4 removeAuthorizedAddressSignature = bytes4(sha3(\"removeAuthorizedAddress(address)\"));\n for (uint i = 0; i < 4; i++) {\n require(data[i] == removeAuthorizedAddressSignature[i]);\n }\n return true;\n }\n}" + "current/multisig/MultiSigWallet/MultiSigWallet.sol": "pragma solidity ^0.4.10;\n\n/// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution.\n/// @author Stefan George - \ncontract MultiSigWallet {\n\n uint constant public MAX_OWNER_COUNT = 50;\n\n event Confirmation(address indexed sender, uint indexed transactionId);\n event Revocation(address indexed sender, uint indexed transactionId);\n event Submission(uint indexed transactionId);\n event Execution(uint indexed transactionId);\n event ExecutionFailure(uint indexed transactionId);\n event Deposit(address indexed sender, uint value);\n event OwnerAddition(address indexed owner);\n event OwnerRemoval(address indexed owner);\n event RequirementChange(uint required);\n\n mapping (uint => Transaction) public transactions;\n mapping (uint => mapping (address => bool)) public confirmations;\n mapping (address => bool) public isOwner;\n address[] public owners;\n uint public required;\n uint public transactionCount;\n\n struct Transaction {\n address destination;\n uint value;\n bytes data;\n bool executed;\n }\n\n modifier onlyWallet() {\n if (msg.sender != address(this))\n throw;\n _;\n }\n\n modifier ownerDoesNotExist(address owner) {\n if (isOwner[owner])\n throw;\n _;\n }\n\n modifier ownerExists(address owner) {\n if (!isOwner[owner])\n throw;\n _;\n }\n\n modifier transactionExists(uint transactionId) {\n if (transactions[transactionId].destination == 0)\n throw;\n _;\n }\n\n modifier confirmed(uint transactionId, address owner) {\n if (!confirmations[transactionId][owner])\n throw;\n _;\n }\n\n modifier notConfirmed(uint transactionId, address owner) {\n if (confirmations[transactionId][owner])\n throw;\n _;\n }\n\n modifier notExecuted(uint transactionId) {\n if (transactions[transactionId].executed)\n throw;\n _;\n }\n\n modifier notNull(address _address) {\n if (_address == 0)\n throw;\n _;\n }\n\n modifier validRequirement(uint ownerCount, uint _required) {\n if ( ownerCount > MAX_OWNER_COUNT\n || _required > ownerCount\n || _required == 0\n || ownerCount == 0)\n throw;\n _;\n }\n\n /// @dev Fallback function allows to deposit ether.\n function()\n payable\n {\n if (msg.value > 0)\n Deposit(msg.sender, msg.value);\n }\n\n /*\n * Public functions\n */\n /// @dev Contract constructor sets initial owners and required number of confirmations.\n /// @param _owners List of initial owners.\n /// @param _required Number of required confirmations.\n function MultiSigWallet(address[] _owners, uint _required)\n public\n validRequirement(_owners.length, _required)\n {\n for (uint i=0; i<_owners.length; i++) {\n if (isOwner[_owners[i]] || _owners[i] == 0)\n throw;\n isOwner[_owners[i]] = true;\n }\n owners = _owners;\n required = _required;\n }\n\n /// @dev Allows to add a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of new owner.\n function addOwner(address owner)\n public\n onlyWallet\n ownerDoesNotExist(owner)\n notNull(owner)\n validRequirement(owners.length + 1, required)\n {\n isOwner[owner] = true;\n owners.push(owner);\n OwnerAddition(owner);\n }\n\n /// @dev Allows to remove an owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner.\n function removeOwner(address owner)\n public\n onlyWallet\n ownerExists(owner)\n {\n isOwner[owner] = false;\n for (uint i=0; i owners.length)\n changeRequirement(owners.length);\n OwnerRemoval(owner);\n }\n\n /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.\n /// @param owner Address of owner to be replaced.\n /// @param owner Address of new owner.\n function replaceOwner(address owner, address newOwner)\n public\n onlyWallet\n ownerExists(owner)\n ownerDoesNotExist(newOwner)\n {\n for (uint i=0; i\ncontract MultiSigWalletWithTimeLock is MultiSigWallet {\n\n event ConfirmationTimeSet(uint indexed transactionId, uint confirmationTime);\n event TimeLockChange(uint secondsTimeLocked);\n\n uint public secondsTimeLocked;\n\n mapping (uint => uint) public confirmationTimes;\n\n modifier notFullyConfirmed(uint transactionId) {\n require(!isConfirmed(transactionId));\n _;\n }\n\n modifier fullyConfirmed(uint transactionId) {\n require(isConfirmed(transactionId));\n _;\n }\n\n modifier pastTimeLock(uint transactionId) {\n require(block.timestamp >= confirmationTimes[transactionId] + secondsTimeLocked);\n _;\n }\n\n /*\n * Public functions\n */\n\n /// @dev Contract constructor sets initial owners, required number of confirmations, and time lock.\n /// @param _owners List of initial owners.\n /// @param _required Number of required confirmations.\n /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.\n function MultiSigWalletWithTimeLock(address[] _owners, uint _required, uint _secondsTimeLocked)\n public\n MultiSigWallet(_owners, _required)\n {\n secondsTimeLocked = _secondsTimeLocked;\n }\n\n /// @dev Changes the duration of the time lock for transactions.\n /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.\n function changeTimeLock(uint _secondsTimeLocked)\n public\n onlyWallet\n {\n secondsTimeLocked = _secondsTimeLocked;\n TimeLockChange(_secondsTimeLocked);\n }\n\n /// @dev Allows an owner to confirm a transaction.\n /// @param transactionId Transaction ID.\n function confirmTransaction(uint transactionId)\n public\n ownerExists(msg.sender)\n transactionExists(transactionId)\n notConfirmed(transactionId, msg.sender)\n notFullyConfirmed(transactionId)\n {\n confirmations[transactionId][msg.sender] = true;\n Confirmation(msg.sender, transactionId);\n if (isConfirmed(transactionId)) {\n setConfirmationTime(transactionId, block.timestamp);\n }\n }\n\n /// @dev Allows an owner to revoke a confirmation for a transaction.\n /// @param transactionId Transaction ID.\n function revokeConfirmation(uint transactionId)\n public\n ownerExists(msg.sender)\n confirmed(transactionId, msg.sender)\n notExecuted(transactionId)\n notFullyConfirmed(transactionId)\n {\n confirmations[transactionId][msg.sender] = false;\n Revocation(msg.sender, transactionId);\n }\n\n /// @dev Allows anyone to execute a confirmed transaction.\n /// @param transactionId Transaction ID.\n function executeTransaction(uint transactionId)\n public\n notExecuted(transactionId)\n fullyConfirmed(transactionId)\n pastTimeLock(transactionId)\n {\n Transaction storage tx = transactions[transactionId];\n tx.executed = true;\n if (tx.destination.call.value(tx.value)(tx.data))\n Execution(transactionId);\n else {\n ExecutionFailure(transactionId);\n tx.executed = false;\n }\n }\n\n /*\n * Internal functions\n */\n\n /// @dev Sets the time of when a submission first passed.\n function setConfirmationTime(uint transactionId, uint confirmationTime)\n internal\n {\n confirmationTimes[transactionId] = confirmationTime;\n ConfirmationTimeSet(transactionId, confirmationTime);\n }\n}\n", + "current/multisig/MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress/MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.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.10;\n\nimport { MultiSigWalletWithTimeLock } from \"../MultiSigWalletWithTimeLock/MultiSigWalletWithTimeLock.sol\";\n\ncontract MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress is MultiSigWalletWithTimeLock {\n\n address public TOKEN_TRANSFER_PROXY_CONTRACT;\n\n modifier validRemoveAuthorizedAddressTx(uint transactionId) {\n Transaction storage tx = transactions[transactionId];\n require(tx.destination == TOKEN_TRANSFER_PROXY_CONTRACT);\n require(isFunctionRemoveAuthorizedAddress(tx.data));\n _;\n }\n\n /// @dev Contract constructor sets initial owners, required number of confirmations, time lock, and tokenTransferProxy address.\n /// @param _owners List of initial owners.\n /// @param _required Number of required confirmations.\n /// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.\n /// @param _tokenTransferProxy Address of TokenTransferProxy contract.\n function MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress(\n address[] _owners,\n uint _required,\n uint _secondsTimeLocked,\n address _tokenTransferProxy)\n public\n MultiSigWalletWithTimeLock(_owners, _required, _secondsTimeLocked)\n {\n TOKEN_TRANSFER_PROXY_CONTRACT = _tokenTransferProxy;\n }\n\n /// @dev Allows execution of removeAuthorizedAddress without time lock.\n /// @param transactionId Transaction ID.\n function executeRemoveAuthorizedAddress(uint transactionId)\n public\n notExecuted(transactionId)\n fullyConfirmed(transactionId)\n validRemoveAuthorizedAddressTx(transactionId)\n {\n Transaction storage tx = transactions[transactionId];\n tx.executed = true;\n if (tx.destination.call.value(tx.value)(tx.data))\n Execution(transactionId);\n else {\n ExecutionFailure(transactionId);\n tx.executed = false;\n }\n }\n\n /// @dev Compares first 4 bytes of byte array to removeAuthorizedAddress function signature.\n /// @param data Transaction data.\n /// @return Successful if data is a call to removeAuthorizedAddress.\n function isFunctionRemoveAuthorizedAddress(bytes data)\n public\n constant\n returns (bool)\n {\n bytes4 removeAuthorizedAddressSignature = bytes4(sha3(\"removeAuthorizedAddress(address)\"));\n for (uint i = 0; i < 4; i++) {\n require(data[i] == removeAuthorizedAddressSignature[i]);\n }\n return true;\n }\n}\n" }, - "sourceTreeHashHex": "0x573413fc71759cd8ee69df44c180fd3960f501349cf046e93ee8b239b4814d35", + "sourceTreeHashHex": "0xdac022bb4f4cd50563fb21da8efb584e698d76b401d3c823cf757a1a2da3aea0", "compiler": { "name": "solc", "version": "soljson-v0.4.24+commit.e67f0147.js", "settings": { + "optimizer": { + "enabled": true, + "runs": 0 + }, "outputSelection": { "*": { "*": [ diff --git a/packages/web3-wrapper/CHANGELOG.json b/packages/web3-wrapper/CHANGELOG.json index b753ffbac0..54a816e232 100644 --- a/packages/web3-wrapper/CHANGELOG.json +++ b/packages/web3-wrapper/CHANGELOG.json @@ -1,4 +1,25 @@ [ + { + "version": "0.7.0", + "changes": [ + { + "note": "Add exported uniqueVersionIds object", + "pr": 622 + }, + { + "note": "Update increaseTimeAsync to work with Geth", + "pr": 622 + }, + { + "note": "Make callAsync throw if raw call result is 0x (null)", + "pr": 622 + }, + { + "note": "Add new setHeadAsync method", + "pr": 622 + } + ] + }, { "timestamp": 1527009133, "version": "0.6.4", diff --git a/packages/web3-wrapper/src/index.ts b/packages/web3-wrapper/src/index.ts index 7309e09a86..b14fa74065 100644 --- a/packages/web3-wrapper/src/index.ts +++ b/packages/web3-wrapper/src/index.ts @@ -1,2 +1,2 @@ -export { Web3Wrapper } from './web3_wrapper'; +export { Web3Wrapper, uniqueVersionIds } from './web3_wrapper'; export { Web3WrapperErrors } from './types'; diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts index 3de152df18..559bf3ea9f 100644 --- a/packages/web3-wrapper/src/web3_wrapper.ts +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -21,6 +21,13 @@ import { Web3WrapperErrors } from './types'; const BASE_TEN = 10; +// These are unique identifiers contained in the response of the +// web3_clientVersion call. +export const uniqueVersionIds = { + geth: 'Geth', + ganache: 'EthereumJS TestRPC', +}; + /** * A wrapper around the Web3.js 0.x library that provides a consistent, clean promise-based interface. */ @@ -254,11 +261,20 @@ export class Web3Wrapper { await this._sendRawPayloadAsync({ method: 'evm_mine', params: [] }); } /** - * Increase the next blocks timestamp on TestRPC/Ganache local node + * Increase the next blocks timestamp on TestRPC/Ganache or Geth local node. + * Will throw if provider is neither TestRPC/Ganache or Geth. * @param timeDelta Amount of time to add in seconds */ - public async increaseTimeAsync(timeDelta: number): Promise { - await this._sendRawPayloadAsync({ method: 'evm_increaseTime', params: [timeDelta] }); + public async increaseTimeAsync(timeDelta: number): Promise { + // Detect Geth vs. Ganache and use appropriate endpoint. + const version = await this.getNodeVersionAsync(); + if (_.includes(version, uniqueVersionIds.geth)) { + return this._sendRawPayloadAsync({ method: 'debug_increaseTime', params: [timeDelta] }); + } else if (_.includes(version, uniqueVersionIds.ganache)) { + return this._sendRawPayloadAsync({ method: 'evm_increaseTime', params: [timeDelta] }); + } else { + throw new Error(`Unknown client version: ${version}`); + } } /** * Retrieve smart contract logs for a given filter @@ -281,7 +297,6 @@ export class Web3Wrapper { }; const payload = { jsonrpc: '2.0', - id: this._jsonRpcRequestId++, method: 'eth_getLogs', params: [serializedFilter], }; @@ -315,6 +330,9 @@ export class Web3Wrapper { */ public async callAsync(callData: CallData, defaultBlock?: BlockParam): Promise { const rawCallResult = await promisify(this._web3.eth.call)(callData, defaultBlock); + if (rawCallResult === '0x') { + throw new Error('Contract call failed (returned null)'); + } return rawCallResult; } /** @@ -403,8 +421,20 @@ export class Web3Wrapper { } return receipt; } + /** + * Calls the 'debug_setHead' JSON RPC method, which sets the current head of + * the local chain by block number. Note, this is a destructive action and + * may severely damage your chain. Use with extreme caution. As of now, this + * is only supported by Geth. It sill throw if the 'debug_setHead' method is + * not supported. + * @param blockNumber The block number to reset to. + */ + public async setHeadAsync(blockNumber: number): Promise { + await this._sendRawPayloadAsync({ method: 'debug_setHead', params: [this._web3.toHex(blockNumber)] }); + } private async _sendRawPayloadAsync(payload: Partial): Promise { const sendAsync = this._web3.currentProvider.sendAsync.bind(this._web3.currentProvider); + payload.id = this._jsonRpcRequestId++; const response = await promisify(sendAsync)(payload); const result = response.result; return result; diff --git a/yarn.lock b/yarn.lock index 4d4552ffad..4fdd5ddbed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4068,14 +4068,7 @@ ethereum-common@^0.0.18: version "0.0.18" resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" -ethereumjs-abi@^0.6.4: - version "0.6.5" - resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz#5a637ef16ab43473fa72a29ad90871405b3f5241" - dependencies: - bn.js "^4.10.0" - ethereumjs-util "^4.3.0" - -"ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git": +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" dependencies: @@ -4124,7 +4117,7 @@ ethereumjs-tx@0xProject/ethereumjs-tx#fake-tx-include-signature-by-default, ethe ethereum-common "^0.0.18" ethereumjs-util "^5.0.0" -ethereumjs-util@^4.0.1, ethereumjs-util@^4.3.0, ethereumjs-util@^4.4.0: +ethereumjs-util@^4.0.1, ethereumjs-util@^4.4.0: version "4.5.0" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz#3e9428b317eebda3d7260d854fddda954b1f1bc6" dependencies: @@ -4190,9 +4183,9 @@ ethereumjs-wallet@~0.6.0: utf8 "^2.1.1" uuid "^2.0.1" -ethers@^3.0.15: - version "3.0.15" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-3.0.15.tgz#7cdea4e23025681f69f575bf481b227315e0e7ab" +ethers@0xproject/ethers.js#eip-838-reasons, ethers@^3.0.15: + version "3.0.18" + resolved "https://codeload.github.com/0xproject/ethers.js/tar.gz/b91342bd200d142af0165d6befddf783c8ae8447" dependencies: aes-js "3.0.0" bn.js "^4.4.0" @@ -12569,7 +12562,7 @@ web3-net@1.0.0-beta.34: web3-core-method "1.0.0-beta.34" web3-utils "1.0.0-beta.34" -web3-provider-engine@^13.3.2: +web3-provider-engine@^13.3.2, web3-provider-engine@^13.6.5: version "13.8.0" resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-13.8.0.tgz#4c7c1ad2af5f1fe10343b8a65495879a2f9c00df" dependencies: @@ -12593,30 +12586,6 @@ web3-provider-engine@^13.3.2: xhr "^2.2.0" xtend "^4.0.1" -web3-provider-engine@^13.6.5: - version "13.6.6" - resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-13.6.6.tgz#7d8972ffcd31e103bd2ce8a521b1b7da08cb173f" - dependencies: - async "^2.5.0" - clone "^2.0.0" - eth-block-tracker "^2.2.2" - eth-sig-util "^1.4.2" - ethereumjs-block "^1.2.2" - ethereumjs-tx "^1.2.0" - ethereumjs-util "^5.1.1" - ethereumjs-vm "^2.0.2" - fetch-ponyfill "^4.0.0" - json-rpc-error "^2.0.0" - json-stable-stringify "^1.0.1" - promise-to-callback "^1.0.0" - readable-stream "^2.2.9" - request "^2.67.0" - semaphore "^1.0.3" - solc "^0.4.2" - tape "^4.4.0" - xhr "^2.2.0" - xtend "^4.0.1" - web3-provider-engine@^14.0.4: version "14.0.4" resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-14.0.4.tgz#6f96b71ea1b3a76cc67cd52007116c8d4b64465b"