From 2a2c7b9df53f7a8b8172b102242ac7b0dd24df6b Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Tue, 14 Apr 2020 13:29:18 +1000 Subject: [PATCH 01/11] ERC20BridgeSampler: Sample Curve Buy --- .../contracts/src/interfaces/ICurve.sol | 1 - contracts/erc20-bridge-sampler/CHANGELOG.json | 3 + .../contracts/src/ERC20BridgeSampler.sol | 37 +++++++ .../contracts/src/IERC20BridgeSampler.sol | 21 +++- .../bridge_sampler_mainnet_test.ts | 39 ++++++- .../utils/market_operation_utils/constants.ts | 10 +- .../sampler_operations.ts | 91 +++++++++++----- packages/contract-artifacts/CHANGELOG.json | 3 + .../artifacts/ERC20BridgeSampler.json | 36 ++++++- .../artifacts/IERC20BridgeSampler.json | 32 +++++- packages/contract-wrappers/CHANGELOG.json | 3 + .../erc20_bridge_sampler.ts | 100 +++++++++++++++++- .../i_erc20_bridge_sampler.ts | 76 ++++++++++++- 13 files changed, 408 insertions(+), 44 deletions(-) diff --git a/contracts/asset-proxy/contracts/src/interfaces/ICurve.sol b/contracts/asset-proxy/contracts/src/interfaces/ICurve.sol index ac2645106b..5ca526364c 100644 --- a/contracts/asset-proxy/contracts/src/interfaces/ICurve.sol +++ b/contracts/asset-proxy/contracts/src/interfaces/ICurve.sol @@ -65,7 +65,6 @@ interface ICurve { returns (uint256 dy); /// @dev Get the amount of `fromToken` by buying `buyAmount` of `toToken` - /// This function exists on later versions of Curve (USDC/DAI/USDT) /// @param i The token index being sold. /// @param j The token index being bought. /// @param buyAmount The amount of token being bought. diff --git a/contracts/erc20-bridge-sampler/CHANGELOG.json b/contracts/erc20-bridge-sampler/CHANGELOG.json index 897b9a65b7..7b85e2f96c 100644 --- a/contracts/erc20-bridge-sampler/CHANGELOG.json +++ b/contracts/erc20-bridge-sampler/CHANGELOG.json @@ -5,6 +5,9 @@ { "note": "Pass in `DevUtils` address as a constructor parameter", "pr": 2531 + }, + { + "note": "Sample `Curve` by buy amounts" } ] }, diff --git a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol index 24eb17c99a..ba2a3826ab 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol @@ -442,6 +442,43 @@ contract ERC20BridgeSampler is makerTokenAmounts[i] = buyAmount; } } + /// @dev Sample buy quotes from Curve. + /// @param curveAddress Address of the Curve contract. + /// @param fromTokenIdx Index of the taker token (what to sell). + /// @param toTokenIdx Index of the maker token (what to buy). + /// @param makerTokenAmounts Maker token buy amount for each sample. + /// @return takerTokenAmounts Taker amounts sold at each maker token + /// amount. + function sampleBuysFromCurve( + address curveAddress, + int128 fromTokenIdx, + int128 toTokenIdx, + uint256[] memory makerTokenAmounts + ) + public + view + returns (uint256[] memory takerTokenAmounts) + { + uint256 numSamples = makerTokenAmounts.length; + takerTokenAmounts = new uint256[](numSamples); + for (uint256 i = 0; i < numSamples; i++) { + (bool didSucceed, bytes memory resultData) = + curveAddress.staticcall.gas(CURVE_CALL_GAS)( + abi.encodeWithSelector( + ICurve(0).get_dx_underlying.selector, + fromTokenIdx, + toTokenIdx, + makerTokenAmounts[i] + )); + uint256 sellAmount = 0; + if (didSucceed) { + sellAmount = abi.decode(resultData, (uint256)); + } else { + break; + } + takerTokenAmounts[i] = sellAmount; + } + } /// @dev Sample sell quotes from an arbitrary on-chain liquidity provider. /// @param registryAddress Address of the liquidity provider registry contract. diff --git a/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol b/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol index bdc0cb78a9..c6650a336a 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol @@ -106,7 +106,7 @@ interface IERC20BridgeSampler { /// @dev Sample buy quotes from Uniswap. /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). - /// @param makerTokenAmounts Maker token sell amount for each sample. + /// @param makerTokenAmounts Maker token buy amount for each sample. /// @return takerTokenAmounts Taker amounts sold at each maker token /// amount. function sampleBuysFromUniswap( @@ -121,7 +121,7 @@ interface IERC20BridgeSampler { /// @dev Sample buy quotes from Eth2Dai/Oasis. /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). - /// @param takerTokenAmounts Maker token sell amount for each sample. + /// @param makerTokenAmounts Maker token buy amount for each sample. /// @return takerTokenAmounts Taker amounts sold at each maker token /// amount. function sampleBuysFromEth2Dai( @@ -150,6 +150,23 @@ interface IERC20BridgeSampler { view returns (uint256[] memory makerTokenAmounts); + /// @dev Sample buy quotes from Curve. + /// @param curveAddress Address of the Curve contract. + /// @param fromTokenIdx Index of the taker token (what to sell). + /// @param toTokenIdx Index of the maker token (what to buy). + /// @param makerTokenAmounts Maker token buy amount for each sample. + /// @return takerTokenAmounts Taker amounts sold at each maker token + /// amount. + function sampleBuysFromCurve( + address curveAddress, + int128 fromTokenIdx, + int128 toTokenIdx, + uint256[] calldata makerTokenAmounts + ) + external + view + returns (uint256[] memory takerTokenAmounts); + /// @dev Sample sell quotes from an arbitrary on-chain liquidity provider. /// @param registryAddress Address of the liquidity provider registry contract. /// @param takerToken Address of the taker token (what to sell). diff --git a/contracts/integrations/test/bridge_sampler/bridge_sampler_mainnet_test.ts b/contracts/integrations/test/bridge_sampler/bridge_sampler_mainnet_test.ts index d0c4f108cc..5781f52191 100644 --- a/contracts/integrations/test/bridge_sampler/bridge_sampler_mainnet_test.ts +++ b/contracts/integrations/test/bridge_sampler/bridge_sampler_mainnet_test.ts @@ -2,23 +2,30 @@ import { artifacts, ERC20BridgeSamplerContract } from '@0x/contracts-erc20-bridg import { blockchainTests, constants, describe, expect, toBaseUnitAmount } from '@0x/contracts-test-utils'; import { BigNumber } from '@0x/utils'; +export const VB = '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b'; +blockchainTests.configure({ + fork: { + unlockedAccounts: [VB], + }, +}); + blockchainTests.fork.resets('Mainnet Sampler Tests', env => { let testContract: ERC20BridgeSamplerContract; before(async () => { testContract = await ERC20BridgeSamplerContract.deployFrom0xArtifactAsync( artifacts.ERC20BridgeSampler, env.provider, - { ...env.txDefaults, from: '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b' }, + { ...env.txDefaults, from: VB }, {}, constants.NULL_ADDRESS, ); }); - describe('sampleSellsFromCurve()', () => { - const curveAddress = '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51'; - const daiTokenIdx = new BigNumber(0); - const usdcTokenIdx = new BigNumber(1); + const curveAddress = '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56'; + const daiTokenIdx = new BigNumber(0); + const usdcTokenIdx = new BigNumber(1); + describe('sampleSellsFromCurve()', () => { it('samples sells from Curve DAI->USDC', async () => { const samples = await testContract .sampleSellsFromCurve(curveAddress, daiTokenIdx, usdcTokenIdx, [toBaseUnitAmount(1)]) @@ -35,4 +42,26 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => { expect(samples[0]).to.be.bignumber.greaterThan(0); }); }); + + describe('sampleBuysFromCurve()', () => { + it('samples buys from Curve DAI->USDC', async () => { + // From DAI to USDC + // I want 1 to buy USDC + const samples = await testContract + .sampleBuysFromCurve(curveAddress, daiTokenIdx, usdcTokenIdx, [toBaseUnitAmount(1, 6)]) + .callAsync(); + expect(samples.length).to.be.bignumber.greaterThan(0); + expect(samples[0]).to.be.bignumber.greaterThan(0); + }); + + it('samples buys from Curve USDC->DAI', async () => { + // From USDC to DAI + // I want 1 to buy 1 DAI + const samples = await testContract + .sampleBuysFromCurve(curveAddress, usdcTokenIdx, daiTokenIdx, [toBaseUnitAmount(1)]) + .callAsync(); + expect(samples.length).to.be.bignumber.greaterThan(0); + expect(samples[0]).to.be.bignumber.greaterThan(0); + }); + }); }); diff --git a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts index 392edbe432..bfeb729279 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -21,7 +21,15 @@ export const SELL_SOURCES = [ /** * Valid sources for market buy. */ -export const BUY_SOURCES = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai]; +export const BUY_SOURCES = [ + ERC20BridgeSource.Uniswap, + ERC20BridgeSource.Eth2Dai, + // All Curve sources + ERC20BridgeSource.CurveUsdcDai, + ERC20BridgeSource.CurveUsdcDaiUsdt, + ERC20BridgeSource.CurveUsdcDaiUsdtBusd, + ERC20BridgeSource.CurveUsdcDaiUsdtTusd, +]; export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = { // tslint:disable-next-line: custom-no-magic-numbers diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts index c0b5550b08..9885f8ca0c 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts @@ -152,6 +152,28 @@ export const samplerOperations = { }, }; }, + getCurveBuyQuotes( + curveAddress: string, + fromTokenIdx: number, + toTokenIdx: number, + makerFillAmounts: BigNumber[], + ): BatchedOperation { + return { + encodeCall: contract => { + return contract + .sampleBuysFromCurve( + curveAddress, + new BigNumber(fromTokenIdx), + new BigNumber(toTokenIdx), + makerFillAmounts, + ) + .getABIEncodedTransactionData(); + }, + handleCallResultsAsync: async (contract, callResults) => { + return contract.getABIDecodedReturnData('sampleBuysFromCurve', callResults); + }, + }; + }, getUniswapBuyQuotes( makerToken: string, takerToken: string, @@ -328,42 +350,63 @@ export const samplerOperations = { makerFillAmounts: BigNumber[], liquidityProviderRegistryAddress?: string | undefined, ): BatchedOperation { - const subOps = sources.map(source => { - if (source === ERC20BridgeSource.Eth2Dai) { - return samplerOperations.getEth2DaiBuyQuotes(makerToken, takerToken, makerFillAmounts); - } else if (source === ERC20BridgeSource.Uniswap) { - return samplerOperations.getUniswapBuyQuotes(makerToken, takerToken, makerFillAmounts); - } else if (source === ERC20BridgeSource.LiquidityProvider) { - if (liquidityProviderRegistryAddress === undefined) { - throw new Error( - 'Cannot sample liquidity from a LiquidityProvider liquidity pool, if a registry is not provided.', + const subOps = sources + .map(source => { + let batchedOperation; + if (source === ERC20BridgeSource.Eth2Dai) { + batchedOperation = samplerOperations.getEth2DaiBuyQuotes(makerToken, takerToken, makerFillAmounts); + } else if (source === ERC20BridgeSource.Uniswap) { + batchedOperation = samplerOperations.getUniswapBuyQuotes(makerToken, takerToken, makerFillAmounts); + } else if (Object.keys(DEFAULT_CURVE_OPTS).includes(source)) { + const { curveAddress, tokens } = DEFAULT_CURVE_OPTS[source]; + const fromTokenIdx = tokens.indexOf(takerToken); + const toTokenIdx = tokens.indexOf(makerToken); + if (fromTokenIdx !== -1 && toTokenIdx !== -1) { + batchedOperation = samplerOperations.getCurveSellQuotes( + curveAddress, + fromTokenIdx, + toTokenIdx, + makerFillAmounts, + ); + } + } else if (source === ERC20BridgeSource.LiquidityProvider) { + if (liquidityProviderRegistryAddress === undefined) { + throw new Error( + 'Cannot sample liquidity from a LiquidityProvider liquidity pool, if a registry is not provided.', + ); + } + batchedOperation = samplerOperations.getLiquidityProviderBuyQuotes( + liquidityProviderRegistryAddress, + makerToken, + takerToken, + makerFillAmounts, ); + } else { + throw new Error(`Unsupported buy sample source: ${source}`); } - return samplerOperations.getLiquidityProviderBuyQuotes( - liquidityProviderRegistryAddress, - makerToken, - takerToken, - makerFillAmounts, - ); - } else { - throw new Error(`Unsupported buy sample source: ${source}`); - } - }); + return { source, batchedOperation }; + }) + .filter(op => op.batchedOperation) as Array<{ + batchedOperation: BatchedOperation; + source: ERC20BridgeSource; + }>; return { encodeCall: contract => { - const subCalls = subOps.map(op => op.encodeCall(contract)); + const subCalls = subOps.map(op => op.batchedOperation.encodeCall(contract)); return contract.batchCall(subCalls).getABIEncodedTransactionData(); }, handleCallResultsAsync: async (contract, callResults) => { const rawSubCallResults = contract.getABIDecodedReturnData('batchCall', callResults); const samples = await Promise.all( - subOps.map(async (op, i) => op.handleCallResultsAsync(contract, rawSubCallResults[i])), + subOps.map(async (op, i) => + op.batchedOperation.handleCallResultsAsync(contract, rawSubCallResults[i]), + ), ); - return sources.map((source, i) => { + return subOps.map((op, i) => { return samples[i].map((output, j) => ({ - source, + source: op.source, output, - input: makerFillAmounts[j], + input: makerFillAmounts[i], })); }); }, diff --git a/packages/contract-artifacts/CHANGELOG.json b/packages/contract-artifacts/CHANGELOG.json index 53691f24f3..615aff17ff 100644 --- a/packages/contract-artifacts/CHANGELOG.json +++ b/packages/contract-artifacts/CHANGELOG.json @@ -9,6 +9,9 @@ { "note": "Added `Forwarder.marketSellAmountWithEth`", "pr": 2521 + }, + { + "note": "Added `ERC20BridgeSampler.sampleBuysFromCurve`" } ] }, diff --git a/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json b/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json index f4f7a148e0..d746744107 100644 --- a/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json +++ b/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json @@ -3,6 +3,12 @@ "contractName": "ERC20BridgeSampler", "compilerOutput": { "abi": [ + { + "inputs": [{ "internalType": "address", "name": "devUtilsAddress", "type": "address" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, { "constant": true, "inputs": [{ "internalType": "bytes[]", "name": "callDatas", "type": "bytes[]" }], @@ -93,6 +99,20 @@ "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [ + { "internalType": "address", "name": "curveAddress", "type": "address" }, + { "internalType": "int128", "name": "fromTokenIdx", "type": "int128" }, + { "internalType": "int128", "name": "toTokenIdx", "type": "int128" }, + { "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" } + ], + "name": "sampleBuysFromCurve", + "outputs": [{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [ @@ -232,6 +252,16 @@ }, "return": "orderFillableTakerAssetAmounts How much taker asset can be filled by each order in `orders`." }, + "sampleBuysFromCurve(address,int128,int128,uint256[])": { + "details": "Sample buy quotes from Curve.", + "params": { + "curveAddress": "Address of the Curve contract.", + "fromTokenIdx": "Index of the taker token (what to sell).", + "makerTokenAmounts": "Maker token buy amount for each sample.", + "toTokenIdx": "Index of the maker token (what to buy)." + }, + "return": "takerTokenAmounts Taker amounts sold at each maker token amount." + }, "sampleBuysFromEth2Dai(address,address,uint256[])": { "details": "Sample buy quotes from Eth2Dai/Oasis.", "params": { @@ -311,16 +341,16 @@ }, "evm": { "bytecode": { - "object": "0x608060405234801561001057600080fd5b50612a0d806100206000396000f3fe608060405234801561001057600080fd5b50600436106100d45760003560e01c806368be3cf2116100815780639f76ad351161005b5780639f76ad35146101a7578063a2c28d4b146101ba578063c7f7142e146101cd576100d4565b806368be3cf2146101615780636dd6b78d146101815780638b123a0214610194576100d4565b806359f515d0116100b257806359f515d01461012857806360ee052a1461013b57806364ee6ade1461014e576100d4565b8063354152a3146100d95780634703a7e6146101025780634cb8e25314610115575b600080fd5b6100ec6100e736600461237c565b6101ed565b6040516100f991906126e3565b60405180910390f35b6100ec61011036600461231c565b6103be565b6100ec61012336600461231c565b61059a565b6100ec610136366004612426565b61088a565b6100ec61014936600461231c565b610933565b6100ec61015c36600461231c565b610bf3565b61017461016f3660046123ba565b610db9565b6040516100f99190612665565b6100ec61018f36600461231c565b610ef8565b6100ec6101a2366004612426565b6111a0565b6100ec6101b53660046122a9565b611468565b6100ec6101c83660046122a9565b61166d565b6101e06101db36600461225f565b611859565b6040516100f991906125ec565b60606000825190508060405190808252806020026020018201604052801561021f578160200160208202803883390190505b50915060005b818110156103b457600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a888151811061027c57fe5b602002602001015160405160240161029693929190612756565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161031f91906125d0565b6000604051808303818686fa925050503d806000811461035b576040519150601f19603f3d011682016040523d82523d6000602084013e610360565b606091505b5090925090506000821561038957818060200190516103829190810190612544565b9050610391565b5050506103b4565b8086858151811061039e57fe5b6020908102919091010152505050600101610225565b5050949350505050565b60606103ca8385611994565b81516040805182815260208084028201019091528180156103f5578160200160208202803883390190505b50915060005b81811015610591576000606061040f611a07565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a888151811061045957fe5b602002602001015160405160240161047393929190612634565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516104fc91906125d0565b6000604051808303818686fa925050503d8060008114610538576040519150601f19603f3d011682016040523d82523d6000602084013e61053d565b606091505b50909250905060008215610566578180602001905161055f9190810190612544565b905061056e565b505050610591565b8086858151811061057b57fe5b60209081029190910101525050506001016103fb565b50509392505050565b60606105a68385611994565b60006105b0611a1f565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146105e857846105fe565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b9050600061060a611a1f565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146106425784610658565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b9050600061066587611a37565b60ff169050600061067587611a37565b60ff169050600086519050806040519080825280602002602001820160405280156106aa578160200160208202803883390190505b50955060005b8181101561087d57600060606106c4611a48565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b8a8a8e888151811061070e57fe5b602002602001015160405160240161072893929190612634565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516107b191906125d0565b6000604051808303818686fa925050503d80600081146107ed576040519150601f19603f3d011682016040523d82523d6000602084013e6107f2565b606091505b5090925090506000821561081b57818060200190516108149190810190612544565b9050610823565b50505061087d565b670de0b6b3a764000087600a0a87600a0a8d878151811061084057fe5b60200260200101518402028161085257fe5b048161085a57fe5b048a858151811061086757fe5b60209081029190910101525050506001016106b0565b5050505050509392505050565b606061089683836111a0565b905060005b835181101561092c578181815181106108b057fe5b60200260200101516000146109245761090b8282815181106108ce57fe5b60200260200101518583815181106108e257fe5b602002602001015160a001518684815181106108fa57fe5b602002602001015160800151611a60565b82828151811061091757fe5b6020026020010181815250505b60010161089b565b5092915050565b606061093f8385611994565b815160408051828152602080840282010190915281801561096a578160200160208202803883390190505b5091506000610977611a1f565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146109b7576109b286611aa2565b6109ba565b60005b905060006109c6611a1f565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610a0657610a0186611aa2565b610a09565b60005b905060005b83811015610be8576001610a20611a1f565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415610ab7578651610a969085907f2640f62c00000000000000000000000000000000000000000000000000000000908a9086908110610a8957fe5b6020026020010151611b34565b878481518110610aa257fe5b60200260200101819350828152505050610bd4565b610abf611a1f565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415610b28578651610a969084907f59e9486200000000000000000000000000000000000000000000000000000000908a9086908110610a8957fe5b8651600090610b619085907f59e9486200000000000000000000000000000000000000000000000000000000908b9087908110610a8957fe5b925090508015610bb757610b96857f2640f62c0000000000000000000000000000000000000000000000000000000083611b34565b888581518110610ba257fe5b60200260200101819450828152505050610bd2565b6000878481518110610bc557fe5b6020026020010181815250505b505b80610bdf5750610be8565b50600101610a0e565b505050509392505050565b6060610bff8385611994565b8151604080518281526020808402820101909152818015610c2a578160200160208202803883390190505b50915060005b818110156105915760006060610c44611a07565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a8881518110610c8e57fe5b6020026020010151604051602401610ca893929190612634565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610d3191906125d0565b6000604051808303818686fa925050503d8060008114610d6d576040519150601f19603f3d011682016040523d82523d6000602084013e610d72565b606091505b509092509050600082156105665781806020019051610d949190810190612544565b905080868581518110610da357fe5b6020908102919091010152505050600101610c30565b604080518281526020808402820101909152606090828015610def57816020015b6060815260200190600190039081610dda5790505b50905060005b80831461092c576000606030868685818110610e0d57fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe136849003018112610e4757600080fd5b9091016020810191503567ffffffffffffffff811115610e6657600080fd5b36819003821315610e7657600080fd5b604051610e849291906125c0565b600060405180830381855afa9150503d8060008114610ebf576040519150601f19603f3d011682016040523d82523d6000602084013e610ec4565b606091505b509150915081610ed657805160208201fd5b80848481518110610ee357fe5b60209081029190910101525050600101610df5565b6060610f048385611994565b8151604080518281526020808402820101909152818015610f2f578160200160208202803883390190505b5091506000610f3c611a1f565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610f7c57610f7786611aa2565b610f7f565b60005b90506000610f8b611a1f565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610fcb57610fc686611aa2565b610fce565b60005b905060005b83811015610be8576001610fe5611a1f565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16141561106f57865161104e9085907f95b68fe700000000000000000000000000000000000000000000000000000000908a9086908110610a8957fe5b87848151811061105a57fe5b6020026020010181935082815250505061118c565b611077611a1f565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156110e057865161104e9084907fcd7724c300000000000000000000000000000000000000000000000000000000908a9086908110610a8957fe5b86516000906111199086907f95b68fe700000000000000000000000000000000000000000000000000000000908b9087908110610a8957fe5b92509050801561116f5761114e847fcd7724c30000000000000000000000000000000000000000000000000000000083611b34565b88858151811061115a57fe5b6020026020010181945082815250505061118a565b600087848151811061117d57fe5b6020026020010181815250505b505b806111975750610be8565b50600101610fd3565b606082516040519080825280602002602001820160405280156111cd578160200160208202803883390190505b50905060005b8351811461092c578281815181106111e757fe5b60200260200101515160001480611215575083818151811061120557fe5b6020026020010151608001516000145b80611237575083818151811061122757fe5b602002602001015160a001516000145b1561125b57600082828151811061124a57fe5b602002602001018181525050611460565b60006060611267611c7d565b73ffffffffffffffffffffffffffffffffffffffff166207a120611289611c7d565b5087517fe77286eb00000000000000000000000000000000000000000000000000000000908990879081106112ba57fe5b60200260200101518887815181106112ce57fe5b60200260200101516040516024016112e79291906127d1565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161137091906125d0565b6000604051808303818686fa925050503d80600081146113ac576040519150601f19603f3d011682016040523d82523d6000602084013e6113b1565b606091505b5091509150816113dc5760008484815181106113c957fe5b6020026020010181815250505050611460565b6113e4611f5b565b600080838060200190516113fb91908101906124d7565b9194509250905060038351600681111561141157fe5b14158061141c575080155b1561144057600087878151811061142f57fe5b60200260200101818152505061145a565b8187878151811061144d57fe5b6020026020010181815250505b50505050505b6001016111d3565b60606000825190508060405190808252806020026020018201604052801561149a578160200160208202803883390190505b50915060006114aa878787611859565b905073ffffffffffffffffffffffffffffffffffffffff81166114cf57506116659050565b60005b8281101561166157600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b888151811061152957fe5b602002602001015160405160240161154393929190612634565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516115cc91906125d0565b6000604051808303818686fa925050503d8060008114611608576040519150601f19603f3d011682016040523d82523d6000602084013e61160d565b606091505b50909250905060008215611636578180602001905161162f9190810190612544565b905061163e565b505050611661565b8087858151811061164b57fe5b60209081029190910101525050506001016114d2565b5050505b949350505050565b60606000825190508060405190808252806020026020018201604052801561169f578160200160208202803883390190505b50915060006116af878787611859565b905073ffffffffffffffffffffffffffffffffffffffff81166116d457506116659050565b60005b8281101561166157600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff166345060eb0905060e01b8b8b8b888151811061172e57fe5b602002602001015160405160240161174893929190612634565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516117d191906125d0565b6000604051808303818686fa925050503d806000811461180d576040519150601f19603f3d011682016040523d82523d6000602084013e611812565b606091505b5090925090506000821561163657818060200190516118349190810190612544565b90508087858151811061184357fe5b60209081029190910101525050506001016116d7565b6040516000906060907f153f59970000000000000000000000000000000000000000000000000000000090611894908690869060240161260d565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff168360405161191c91906125d0565b600060405180830381855afa9150503d8060008114611957576040519150601f19603f3d011682016040523d82523d6000602084013e61195c565b606091505b509150915081801561196f575080516020145b156119895761197f81600c611c95565b935050505061198d565b5050505b9392505050565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611a03576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119fa90612774565b60405180910390fd5b5050565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b6000611a4282611cda565b92915050565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b600061166583611a96611a7a82600163ffffffff611dab16565b611a8a888763ffffffff611dca16565b9063ffffffff611dfb16565b9063ffffffff611e1716565b6000611aac611e41565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b8152600401611ae491906125ec565b60206040518083038186803b158015611afc57600080fd5b505afa158015611b10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a429190810190612243565b60008073ffffffffffffffffffffffffffffffffffffffff8516611b5757611c75565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f08686604051602401611b869190612925565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611c0f91906125d0565b6000604051808303818686fa925050503d8060008114611c4b576040519150601f19603f3d011682016040523d82523d6000602084013e611c50565b606091505b5090925090508115611c735780806020019051611c709190810190612544565b92505b505b935093915050565b7374134cf88b21383713e096a5ecf59e297dc7f54790565b60008160140183511015611cbb57611cbb611cb66004855185601401611e59565b611efe565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce56700000000000000000000000000000000000000000000000000000000815250604051611d3e91906125d0565b600060405180830381855afa9150503d8060008114611d79576040519150601f19603f3d011682016040523d82523d6000602084013e611d7e565b606091505b5091509150818015611d91575080516020145b15611da457611da1816000611f06565b92505b5050919050565b600082821115611dc457611dc4611cb660028585611f12565b50900390565b600082611dd957506000611a42565b82820282848281611de657fe5b041461198d5761198d611cb660018686611f12565b60008282018381101561198d5761198d611cb660008686611f12565b600081611e2d57611e2d611cb660038585611f12565b6000828481611e3857fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b6060632800659560e01b848484604051602401611e7893929190612748565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b600061198d8383611f31565b606063e946c1bb60e01b848484604051602401611e7893929190612726565b60008160200183511015611f5257611f52611cb66005855185602001611e59565b50016020015190565b6040805160608101909152806000815260006020820181905260409091015290565b8035611a42816129a5565b600082601f830112611f98578081fd5b8135611fab611fa682612955565b61292e565b8181529150602080830190840160005b83811015611fe857611fd3876020843589010161205b565b83526020928301929190910190600101611fbb565b5050505092915050565b600082601f830112612002578081fd5b8135612010611fa682612955565b81815291506020808301908481018184028601820187101561203157600080fd5b60005b8481101561205057813584529282019290820190600101612034565b505050505092915050565b600082601f83011261206b578081fd5b813567ffffffffffffffff811115612081578182fd5b6120b260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161292e565b91508082528360208285010111156120c957600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b8114611a4257600080fd5b60006101c0808385031215612107578182fd5b6121108161292e565b91505061211d8383611f7d565b815261212c8360208401611f7d565b602082015261213e8360408401611f7d565b60408201526121508360608401611f7d565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff808211156121b257600080fd5b6121be8683870161205b565b838501526101609250828501359150808211156121da57600080fd5b6121e68683870161205b565b8385015261018092508285013591508082111561220257600080fd5b61220e8683870161205b565b838501526101a092508285013591508082111561222a57600080fd5b506122378582860161205b565b82840152505092915050565b600060208284031215612254578081fd5b815161198d816129a5565b600080600060608486031215612273578182fd5b833561227e816129a5565b9250602084013561228e816129a5565b9150604084013561229e816129a5565b809150509250925092565b600080600080608085870312156122be578081fd5b84356122c9816129a5565b935060208501356122d9816129a5565b925060408501356122e9816129a5565b9150606085013567ffffffffffffffff811115612304578182fd5b61231087828801611ff2565b91505092959194509250565b600080600060608486031215612330578283fd5b833561233b816129a5565b9250602084013561234b816129a5565b9150604084013567ffffffffffffffff811115612366578182fd5b61237286828701611ff2565b9150509250925092565b60008060008060808587031215612391578182fd5b843561239c816129a5565b93506123ab86602087016120e2565b92506122e986604087016120e2565b600080602083850312156123cc578182fd5b823567ffffffffffffffff808211156123e3578384fd5b81850186601f8201126123f4578485fd5b8035925081831115612404578485fd5b8660208085028301011115612417578485fd5b60200196919550909350505050565b60008060408385031215612438578182fd5b823567ffffffffffffffff8082111561244f578384fd5b81850186601f820112612460578485fd5b80359250612470611fa684612955565b83815260208082019190838101885b878110156124a8576124968c8484358901016120f4565b8552938201939082019060010161247f565b509197508801359450505050808211156124c0578283fd5b506124cd85828601611f88565b9150509250929050565b600080600083850360a08112156124ec578182fd5b60608112156124f9578182fd5b50612504606061292e565b845160078110612512578283fd5b81526020858101519082015260408086015190820152606085015160808601519194509250801515811461229e578182fd5b600060208284031215612555578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845261258e816020860160208601612975565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b600082516125e2818460208701612975565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b6000602080830181845280855180835260408601915060408482028701019250838701855b828110156126d6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526126c4858351612576565b9450928501929085019060010161268a565b5092979650505050505050565b602080825282518282018190526000918401906040840190835b8181101561271b5783518352602093840193909201916001016126fd565b509095945050505050565b606081016004851061273457fe5b938152602081019290925260409091015290565b606081016008851061273457fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b6000604082526127e560408301855161255c565b60208401516127f7606084018261255c565b50604084015161280a608084018261255c565b50606084015161281d60a084018261255c565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c09150610180828187015261288e610200870185612576565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a08388830301818901526128cd8287612576565b838b015196508489820301868a01526128e68188612576565b955050808a0151955050505080858303016101e0860152506129088183612576565b848103602086015261291a8187612576565b979650505050505050565b90815260200190565b60405181810167ffffffffffffffff8111828210171561294d57600080fd5b604052919050565b600067ffffffffffffffff82111561296b578081fd5b5060209081020190565b60005b83811015612990578181015183820152602001612978565b8381111561299f576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff811681146129c757600080fd5b5056fea365627a7a7231582034e7eab64809a5ab7420c524f487ac0ca03a80f2b355b2d12a392dde2df0522e6c6578706572696d656e74616cf564736f6c63430005100040" + "object": "0x60806040523480156200001157600080fd5b5060405162002c7838038062002c7883398101604081905262000034916200005a565b600080546001600160a01b0319166001600160a01b03929092169190911790556200008a565b6000602082840312156200006c578081fd5b81516001600160a01b038116811462000083578182fd5b9392505050565b612bde806200009a6000396000f3fe608060405234801561001057600080fd5b50600436106100df5760003560e01c806364ee6ade1161008c5780638b123a02116100665780638b123a02146101b25780639f76ad35146101c5578063a2c28d4b146101d8578063c7f7142e146101eb576100df565b806364ee6ade1461016c57806368be3cf21461017f5780636dd6b78d1461019f576100df565b80634cb8e253116100bd5780634cb8e2531461013357806359f515d01461014657806360ee052a14610159576100df565b80631796fb87146100e4578063354152a31461010d5780634703a7e614610120575b600080fd5b6100f76100f236600461254d565b61020b565b60405161010491906128b4565b60405180910390f35b6100f761011b36600461254d565b6103dc565b6100f761012e3660046124ed565b610596565b6100f76101413660046124ed565b610772565b6100f76101543660046125f7565b610a62565b6100f76101673660046124ed565b610b0b565b6100f761017a3660046124ed565b610dcb565b61019261018d36600461258b565b610f91565b6040516101049190612836565b6100f76101ad3660046124ed565b6110d0565b6100f76101c03660046125f7565b611378565b6100f76101d336600461247a565b611651565b6100f76101e636600461247a565b611856565b6101fe6101f9366004612430565b611a42565b60405161010491906127bd565b60606000825190508060405190808252806020026020018201604052801561023d578160200160208202803883390190505b50915060005b818110156103d257600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff16630e71d1b9905060e01b8a8a8a888151811061029a57fe5b60200260200101516040516024016102b493929190612927565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161033d91906127a1565b6000604051808303818686fa925050503d8060008114610379576040519150601f19603f3d011682016040523d82523d6000602084013e61037e565b606091505b509092509050600082156103a757818060200190516103a09190810190612715565b90506103af565b5050506103d2565b808685815181106103bc57fe5b6020908102919091010152505050600101610243565b5050949350505050565b60606000825190508060405190808252806020026020018201604052801561040e578160200160208202803883390190505b50915060005b818110156103d257600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a888151811061046b57fe5b602002602001015160405160240161048593929190612927565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161050e91906127a1565b6000604051808303818686fa925050503d806000811461054a576040519150601f19603f3d011682016040523d82523d6000602084013e61054f565b606091505b509092509050600082156103a757818060200190516105719190810190612715565b90508086858151811061058057fe5b6020908102919091010152505050600101610414565b60606105a28385611b7d565b81516040805182815260208084028201019091528180156105cd578160200160208202803883390190505b50915060005b8181101561076957600060606105e7611bf0565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a888151811061063157fe5b602002602001015160405160240161064b93929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516106d491906127a1565b6000604051808303818686fa925050503d8060008114610710576040519150601f19603f3d011682016040523d82523d6000602084013e610715565b606091505b5090925090506000821561073e57818060200190516107379190810190612715565b9050610746565b505050610769565b8086858151811061075357fe5b60209081029190910101525050506001016105d3565b50509392505050565b606061077e8385611b7d565b6000610788611c08565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146107c057846107d6565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006107e2611c08565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161461081a5784610830565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b9050600061083d87611c20565b60ff169050600061084d87611c20565b60ff16905060008651905080604051908082528060200260200182016040528015610882578160200160208202803883390190505b50955060005b81811015610a55576000606061089c611c31565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b8a8a8e88815181106108e657fe5b602002602001015160405160240161090093929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161098991906127a1565b6000604051808303818686fa925050503d80600081146109c5576040519150601f19603f3d011682016040523d82523d6000602084013e6109ca565b606091505b509092509050600082156109f357818060200190516109ec9190810190612715565b90506109fb565b505050610a55565b670de0b6b3a764000087600a0a87600a0a8d8781518110610a1857fe5b602002602001015184020281610a2a57fe5b0481610a3257fe5b048a8581518110610a3f57fe5b6020908102919091010152505050600101610888565b5050505050509392505050565b6060610a6e8383611378565b905060005b8351811015610b0457818181518110610a8857fe5b6020026020010151600014610afc57610ae3828281518110610aa657fe5b6020026020010151858381518110610aba57fe5b602002602001015160a00151868481518110610ad257fe5b602002602001015160800151611c49565b828281518110610aef57fe5b6020026020010181815250505b600101610a73565b5092915050565b6060610b178385611b7d565b8151604080518281526020808402820101909152818015610b42578160200160208202803883390190505b5091506000610b4f611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610b8f57610b8a86611c8b565b610b92565b60005b90506000610b9e611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610bde57610bd986611c8b565b610be1565b60005b905060005b83811015610dc0576001610bf8611c08565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415610c8f578651610c6e9085907f2640f62c00000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b6020026020010151611d1d565b878481518110610c7a57fe5b60200260200101819350828152505050610dac565b610c97611c08565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415610d00578651610c6e9084907f59e9486200000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b8651600090610d399085907f59e9486200000000000000000000000000000000000000000000000000000000908b9087908110610c6157fe5b925090508015610d8f57610d6e857f2640f62c0000000000000000000000000000000000000000000000000000000083611d1d565b888581518110610d7a57fe5b60200260200101819450828152505050610daa565b6000878481518110610d9d57fe5b6020026020010181815250505b505b80610db75750610dc0565b50600101610be6565b505050509392505050565b6060610dd78385611b7d565b8151604080518281526020808402820101909152818015610e02578160200160208202803883390190505b50915060005b818110156107695760006060610e1c611bf0565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a8881518110610e6657fe5b6020026020010151604051602401610e8093929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610f0991906127a1565b6000604051808303818686fa925050503d8060008114610f45576040519150601f19603f3d011682016040523d82523d6000602084013e610f4a565b606091505b5090925090506000821561073e5781806020019051610f6c9190810190612715565b905080868581518110610f7b57fe5b6020908102919091010152505050600101610e08565b604080518281526020808402820101909152606090828015610fc757816020015b6060815260200190600190039081610fb25790505b50905060005b808314610b04576000606030868685818110610fe557fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13684900301811261101f57600080fd5b9091016020810191503567ffffffffffffffff81111561103e57600080fd5b3681900382131561104e57600080fd5b60405161105c929190612791565b600060405180830381855afa9150503d8060008114611097576040519150601f19603f3d011682016040523d82523d6000602084013e61109c565b606091505b5091509150816110ae57805160208201fd5b808484815181106110bb57fe5b60209081029190910101525050600101610fcd565b60606110dc8385611b7d565b8151604080518281526020808402820101909152818015611107578160200160208202803883390190505b5091506000611114611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146111545761114f86611c8b565b611157565b60005b90506000611163611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146111a35761119e86611c8b565b6111a6565b60005b905060005b83811015610dc05760016111bd611c08565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614156112475786516112269085907f95b68fe700000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b87848151811061123257fe5b60200260200101819350828152505050611364565b61124f611c08565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156112b85786516112269084907fcd7724c300000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b86516000906112f19086907f95b68fe700000000000000000000000000000000000000000000000000000000908b9087908110610c6157fe5b92509050801561134757611326847fcd7724c30000000000000000000000000000000000000000000000000000000083611d1d565b88858151811061133257fe5b60200260200101819450828152505050611362565b600087848151811061135557fe5b6020026020010181815250505b505b8061136f5750610dc0565b506001016111ab565b606082516040519080825280602002602001820160405280156113a5578160200160208202803883390190505b506000805491925073ffffffffffffffffffffffffffffffffffffffff909116905b84518114611649578381815181106113db57fe5b6020026020010151516000148061140957508481815181106113f957fe5b6020026020010151608001516000145b8061142b575084818151811061141b57fe5b602002602001015160a001516000145b1561144f57600083828151811061143e57fe5b602002602001018181525050611641565b600060608373ffffffffffffffffffffffffffffffffffffffff166207a1208573ffffffffffffffffffffffffffffffffffffffff1663e77286eb905060e01b89868151811061149b57fe5b60200260200101518987815181106114af57fe5b60200260200101516040516024016114c89291906129a2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161155191906127a1565b6000604051808303818686fa925050503d806000811461158d576040519150601f19603f3d011682016040523d82523d6000602084013e611592565b606091505b5091509150816115bd5760008584815181106115aa57fe5b6020026020010181815250505050611641565b6115c561212c565b600080838060200190516115dc91908101906126a8565b919450925090506003835160068111156115f257fe5b1415806115fd575080155b1561162157600088878151811061161057fe5b60200260200101818152505061163b565b8188878151811061162e57fe5b6020026020010181815250505b50505050505b6001016113c7565b505092915050565b606060008251905080604051908082528060200260200182016040528015611683578160200160208202803883390190505b5091506000611693878787611a42565b905073ffffffffffffffffffffffffffffffffffffffff81166116b8575061184e9050565b60005b8281101561184a57600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b888151811061171257fe5b602002602001015160405160240161172c93929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516117b591906127a1565b6000604051808303818686fa925050503d80600081146117f1576040519150601f19603f3d011682016040523d82523d6000602084013e6117f6565b606091505b5090925090506000821561181f57818060200190516118189190810190612715565b9050611827565b50505061184a565b8087858151811061183457fe5b60209081029190910101525050506001016116bb565b5050505b949350505050565b606060008251905080604051908082528060200260200182016040528015611888578160200160208202803883390190505b5091506000611898878787611a42565b905073ffffffffffffffffffffffffffffffffffffffff81166118bd575061184e9050565b60005b8281101561184a57600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff166345060eb0905060e01b8b8b8b888151811061191757fe5b602002602001015160405160240161193193929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516119ba91906127a1565b6000604051808303818686fa925050503d80600081146119f6576040519150601f19603f3d011682016040523d82523d6000602084013e6119fb565b606091505b5090925090506000821561181f5781806020019051611a1d9190810190612715565b905080878581518110611a2c57fe5b60209081029190910101525050506001016118c0565b6040516000906060907f153f59970000000000000000000000000000000000000000000000000000000090611a7d90869086906024016127de565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff1683604051611b0591906127a1565b600060405180830381855afa9150503d8060008114611b40576040519150601f19603f3d011682016040523d82523d6000602084013e611b45565b606091505b5091509150818015611b58575080516020145b15611b7257611b6881600c611e66565b9350505050611b76565b5050505b9392505050565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611bec576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611be390612945565b60405180910390fd5b5050565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b6000611c2b82611eab565b92915050565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b600061184e83611c7f611c6382600163ffffffff611f7c16565b611c73888763ffffffff611f9b16565b9063ffffffff611fcc16565b9063ffffffff611fe816565b6000611c95612012565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b8152600401611ccd91906127bd565b60206040518083038186803b158015611ce557600080fd5b505afa158015611cf9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c2b9190810190612414565b60008073ffffffffffffffffffffffffffffffffffffffff8516611d4057611e5e565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f08686604051602401611d6f9190612af6565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611df891906127a1565b6000604051808303818686fa925050503d8060008114611e34576040519150601f19603f3d011682016040523d82523d6000602084013e611e39565b606091505b5090925090508115611e5c5780806020019051611e599190810190612715565b92505b505b935093915050565b60008160140183511015611e8c57611e8c611e87600485518560140161202a565b6120cf565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce56700000000000000000000000000000000000000000000000000000000815250604051611f0f91906127a1565b600060405180830381855afa9150503d8060008114611f4a576040519150601f19603f3d011682016040523d82523d6000602084013e611f4f565b606091505b5091509150818015611f62575080516020145b15611f7557611f728160006120d7565b92505b5050919050565b600082821115611f9557611f95611e87600285856120e3565b50900390565b600082611faa57506000611c2b565b82820282848281611fb757fe5b0414611b7657611b76611e87600186866120e3565b600082820183811015611b7657611b76611e87600086866120e3565b600081611ffe57611ffe611e87600385856120e3565b600082848161200957fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b6060632800659560e01b84848460405160240161204993929190612919565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b6000611b768383612102565b606063e946c1bb60e01b848484604051602401612049939291906128f7565b6000816020018351101561212357612123611e87600585518560200161202a565b50016020015190565b6040805160608101909152806000815260006020820181905260409091015290565b8035611c2b81612b76565b600082601f830112612169578081fd5b813561217c61217782612b26565b612aff565b8181529150602080830190840160005b838110156121b9576121a4876020843589010161222c565b8352602092830192919091019060010161218c565b5050505092915050565b600082601f8301126121d3578081fd5b81356121e161217782612b26565b81815291506020808301908481018184028601820187101561220257600080fd5b60005b8481101561222157813584529282019290820190600101612205565b505050505092915050565b600082601f83011261223c578081fd5b813567ffffffffffffffff811115612252578182fd5b61228360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601612aff565b915080825283602082850101111561229a57600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b8114611c2b57600080fd5b60006101c08083850312156122d8578182fd5b6122e181612aff565b9150506122ee838361214e565b81526122fd836020840161214e565b602082015261230f836040840161214e565b6040820152612321836060840161214e565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff8082111561238357600080fd5b61238f8683870161222c565b838501526101609250828501359150808211156123ab57600080fd5b6123b78683870161222c565b838501526101809250828501359150808211156123d357600080fd5b6123df8683870161222c565b838501526101a09250828501359150808211156123fb57600080fd5b506124088582860161222c565b82840152505092915050565b600060208284031215612425578081fd5b8151611b7681612b76565b600080600060608486031215612444578182fd5b833561244f81612b76565b9250602084013561245f81612b76565b9150604084013561246f81612b76565b809150509250925092565b6000806000806080858703121561248f578081fd5b843561249a81612b76565b935060208501356124aa81612b76565b925060408501356124ba81612b76565b9150606085013567ffffffffffffffff8111156124d5578182fd5b6124e1878288016121c3565b91505092959194509250565b600080600060608486031215612501578283fd5b833561250c81612b76565b9250602084013561251c81612b76565b9150604084013567ffffffffffffffff811115612537578182fd5b612543868287016121c3565b9150509250925092565b60008060008060808587031215612562578182fd5b843561256d81612b76565b935061257c86602087016122b3565b92506124ba86604087016122b3565b6000806020838503121561259d578182fd5b823567ffffffffffffffff808211156125b4578384fd5b81850186601f8201126125c5578485fd5b80359250818311156125d5578485fd5b86602080850283010111156125e8578485fd5b60200196919550909350505050565b60008060408385031215612609578182fd5b823567ffffffffffffffff80821115612620578384fd5b81850186601f820112612631578485fd5b8035925061264161217784612b26565b83815260208082019190838101885b87811015612679576126678c8484358901016122c5565b85529382019390820190600101612650565b50919750880135945050505080821115612691578283fd5b5061269e85828601612159565b9150509250929050565b600080600083850360a08112156126bd578182fd5b60608112156126ca578182fd5b506126d56060612aff565b8451600781106126e3578283fd5b81526020858101519082015260408086015190820152606085015160808601519194509250801515811461246f578182fd5b600060208284031215612726578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845261275f816020860160208601612b46565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b600082516127b3818460208701612b46565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b6000602080830181845280855180835260408601915060408482028701019250838701855b828110156128a7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452612895858351612747565b9450928501929085019060010161285b565b5092979650505050505050565b602080825282518282018190526000918401906040840190835b818110156128ec5783518352602093840193909201916001016128ce565b509095945050505050565b606081016004851061290557fe5b938152602081019290925260409091015290565b606081016008851061290557fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b6000604082526129b660408301855161272d565b60208401516129c8606084018261272d565b5060408401516129db608084018261272d565b5060608401516129ee60a084018261272d565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c091506101808281870152612a5f610200870185612747565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a0838883030181890152612a9e8287612747565b838b015196508489820301868a0152612ab78188612747565b955050808a0151955050505080858303016101e086015250612ad98183612747565b8481036020860152612aeb8187612747565b979650505050505050565b90815260200190565b60405181810167ffffffffffffffff81118282101715612b1e57600080fd5b604052919050565b600067ffffffffffffffff821115612b3c578081fd5b5060209081020190565b60005b83811015612b61578181015183820152602001612b49565b83811115612b70576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff81168114612b9857600080fd5b5056fea365627a7a72315820fd16ce5ab5cf6ae3213442d55793e044c4eb1e0d181b86e7dc7964a0e1a416f86c6578706572696d656e74616cf564736f6c63430005110040" }, "deployedBytecode": { - "object": "0x608060405234801561001057600080fd5b50600436106100d45760003560e01c806368be3cf2116100815780639f76ad351161005b5780639f76ad35146101a7578063a2c28d4b146101ba578063c7f7142e146101cd576100d4565b806368be3cf2146101615780636dd6b78d146101815780638b123a0214610194576100d4565b806359f515d0116100b257806359f515d01461012857806360ee052a1461013b57806364ee6ade1461014e576100d4565b8063354152a3146100d95780634703a7e6146101025780634cb8e25314610115575b600080fd5b6100ec6100e736600461237c565b6101ed565b6040516100f991906126e3565b60405180910390f35b6100ec61011036600461231c565b6103be565b6100ec61012336600461231c565b61059a565b6100ec610136366004612426565b61088a565b6100ec61014936600461231c565b610933565b6100ec61015c36600461231c565b610bf3565b61017461016f3660046123ba565b610db9565b6040516100f99190612665565b6100ec61018f36600461231c565b610ef8565b6100ec6101a2366004612426565b6111a0565b6100ec6101b53660046122a9565b611468565b6100ec6101c83660046122a9565b61166d565b6101e06101db36600461225f565b611859565b6040516100f991906125ec565b60606000825190508060405190808252806020026020018201604052801561021f578160200160208202803883390190505b50915060005b818110156103b457600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a888151811061027c57fe5b602002602001015160405160240161029693929190612756565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161031f91906125d0565b6000604051808303818686fa925050503d806000811461035b576040519150601f19603f3d011682016040523d82523d6000602084013e610360565b606091505b5090925090506000821561038957818060200190516103829190810190612544565b9050610391565b5050506103b4565b8086858151811061039e57fe5b6020908102919091010152505050600101610225565b5050949350505050565b60606103ca8385611994565b81516040805182815260208084028201019091528180156103f5578160200160208202803883390190505b50915060005b81811015610591576000606061040f611a07565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a888151811061045957fe5b602002602001015160405160240161047393929190612634565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516104fc91906125d0565b6000604051808303818686fa925050503d8060008114610538576040519150601f19603f3d011682016040523d82523d6000602084013e61053d565b606091505b50909250905060008215610566578180602001905161055f9190810190612544565b905061056e565b505050610591565b8086858151811061057b57fe5b60209081029190910101525050506001016103fb565b50509392505050565b60606105a68385611994565b60006105b0611a1f565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146105e857846105fe565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b9050600061060a611a1f565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146106425784610658565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b9050600061066587611a37565b60ff169050600061067587611a37565b60ff169050600086519050806040519080825280602002602001820160405280156106aa578160200160208202803883390190505b50955060005b8181101561087d57600060606106c4611a48565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b8a8a8e888151811061070e57fe5b602002602001015160405160240161072893929190612634565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516107b191906125d0565b6000604051808303818686fa925050503d80600081146107ed576040519150601f19603f3d011682016040523d82523d6000602084013e6107f2565b606091505b5090925090506000821561081b57818060200190516108149190810190612544565b9050610823565b50505061087d565b670de0b6b3a764000087600a0a87600a0a8d878151811061084057fe5b60200260200101518402028161085257fe5b048161085a57fe5b048a858151811061086757fe5b60209081029190910101525050506001016106b0565b5050505050509392505050565b606061089683836111a0565b905060005b835181101561092c578181815181106108b057fe5b60200260200101516000146109245761090b8282815181106108ce57fe5b60200260200101518583815181106108e257fe5b602002602001015160a001518684815181106108fa57fe5b602002602001015160800151611a60565b82828151811061091757fe5b6020026020010181815250505b60010161089b565b5092915050565b606061093f8385611994565b815160408051828152602080840282010190915281801561096a578160200160208202803883390190505b5091506000610977611a1f565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146109b7576109b286611aa2565b6109ba565b60005b905060006109c6611a1f565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610a0657610a0186611aa2565b610a09565b60005b905060005b83811015610be8576001610a20611a1f565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415610ab7578651610a969085907f2640f62c00000000000000000000000000000000000000000000000000000000908a9086908110610a8957fe5b6020026020010151611b34565b878481518110610aa257fe5b60200260200101819350828152505050610bd4565b610abf611a1f565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415610b28578651610a969084907f59e9486200000000000000000000000000000000000000000000000000000000908a9086908110610a8957fe5b8651600090610b619085907f59e9486200000000000000000000000000000000000000000000000000000000908b9087908110610a8957fe5b925090508015610bb757610b96857f2640f62c0000000000000000000000000000000000000000000000000000000083611b34565b888581518110610ba257fe5b60200260200101819450828152505050610bd2565b6000878481518110610bc557fe5b6020026020010181815250505b505b80610bdf5750610be8565b50600101610a0e565b505050509392505050565b6060610bff8385611994565b8151604080518281526020808402820101909152818015610c2a578160200160208202803883390190505b50915060005b818110156105915760006060610c44611a07565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a8881518110610c8e57fe5b6020026020010151604051602401610ca893929190612634565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610d3191906125d0565b6000604051808303818686fa925050503d8060008114610d6d576040519150601f19603f3d011682016040523d82523d6000602084013e610d72565b606091505b509092509050600082156105665781806020019051610d949190810190612544565b905080868581518110610da357fe5b6020908102919091010152505050600101610c30565b604080518281526020808402820101909152606090828015610def57816020015b6060815260200190600190039081610dda5790505b50905060005b80831461092c576000606030868685818110610e0d57fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe136849003018112610e4757600080fd5b9091016020810191503567ffffffffffffffff811115610e6657600080fd5b36819003821315610e7657600080fd5b604051610e849291906125c0565b600060405180830381855afa9150503d8060008114610ebf576040519150601f19603f3d011682016040523d82523d6000602084013e610ec4565b606091505b509150915081610ed657805160208201fd5b80848481518110610ee357fe5b60209081029190910101525050600101610df5565b6060610f048385611994565b8151604080518281526020808402820101909152818015610f2f578160200160208202803883390190505b5091506000610f3c611a1f565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610f7c57610f7786611aa2565b610f7f565b60005b90506000610f8b611a1f565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610fcb57610fc686611aa2565b610fce565b60005b905060005b83811015610be8576001610fe5611a1f565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16141561106f57865161104e9085907f95b68fe700000000000000000000000000000000000000000000000000000000908a9086908110610a8957fe5b87848151811061105a57fe5b6020026020010181935082815250505061118c565b611077611a1f565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156110e057865161104e9084907fcd7724c300000000000000000000000000000000000000000000000000000000908a9086908110610a8957fe5b86516000906111199086907f95b68fe700000000000000000000000000000000000000000000000000000000908b9087908110610a8957fe5b92509050801561116f5761114e847fcd7724c30000000000000000000000000000000000000000000000000000000083611b34565b88858151811061115a57fe5b6020026020010181945082815250505061118a565b600087848151811061117d57fe5b6020026020010181815250505b505b806111975750610be8565b50600101610fd3565b606082516040519080825280602002602001820160405280156111cd578160200160208202803883390190505b50905060005b8351811461092c578281815181106111e757fe5b60200260200101515160001480611215575083818151811061120557fe5b6020026020010151608001516000145b80611237575083818151811061122757fe5b602002602001015160a001516000145b1561125b57600082828151811061124a57fe5b602002602001018181525050611460565b60006060611267611c7d565b73ffffffffffffffffffffffffffffffffffffffff166207a120611289611c7d565b5087517fe77286eb00000000000000000000000000000000000000000000000000000000908990879081106112ba57fe5b60200260200101518887815181106112ce57fe5b60200260200101516040516024016112e79291906127d1565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161137091906125d0565b6000604051808303818686fa925050503d80600081146113ac576040519150601f19603f3d011682016040523d82523d6000602084013e6113b1565b606091505b5091509150816113dc5760008484815181106113c957fe5b6020026020010181815250505050611460565b6113e4611f5b565b600080838060200190516113fb91908101906124d7565b9194509250905060038351600681111561141157fe5b14158061141c575080155b1561144057600087878151811061142f57fe5b60200260200101818152505061145a565b8187878151811061144d57fe5b6020026020010181815250505b50505050505b6001016111d3565b60606000825190508060405190808252806020026020018201604052801561149a578160200160208202803883390190505b50915060006114aa878787611859565b905073ffffffffffffffffffffffffffffffffffffffff81166114cf57506116659050565b60005b8281101561166157600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b888151811061152957fe5b602002602001015160405160240161154393929190612634565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516115cc91906125d0565b6000604051808303818686fa925050503d8060008114611608576040519150601f19603f3d011682016040523d82523d6000602084013e61160d565b606091505b50909250905060008215611636578180602001905161162f9190810190612544565b905061163e565b505050611661565b8087858151811061164b57fe5b60209081029190910101525050506001016114d2565b5050505b949350505050565b60606000825190508060405190808252806020026020018201604052801561169f578160200160208202803883390190505b50915060006116af878787611859565b905073ffffffffffffffffffffffffffffffffffffffff81166116d457506116659050565b60005b8281101561166157600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff166345060eb0905060e01b8b8b8b888151811061172e57fe5b602002602001015160405160240161174893929190612634565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516117d191906125d0565b6000604051808303818686fa925050503d806000811461180d576040519150601f19603f3d011682016040523d82523d6000602084013e611812565b606091505b5090925090506000821561163657818060200190516118349190810190612544565b90508087858151811061184357fe5b60209081029190910101525050506001016116d7565b6040516000906060907f153f59970000000000000000000000000000000000000000000000000000000090611894908690869060240161260d565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff168360405161191c91906125d0565b600060405180830381855afa9150503d8060008114611957576040519150601f19603f3d011682016040523d82523d6000602084013e61195c565b606091505b509150915081801561196f575080516020145b156119895761197f81600c611c95565b935050505061198d565b5050505b9392505050565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611a03576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119fa90612774565b60405180910390fd5b5050565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b6000611a4282611cda565b92915050565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b600061166583611a96611a7a82600163ffffffff611dab16565b611a8a888763ffffffff611dca16565b9063ffffffff611dfb16565b9063ffffffff611e1716565b6000611aac611e41565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b8152600401611ae491906125ec565b60206040518083038186803b158015611afc57600080fd5b505afa158015611b10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a429190810190612243565b60008073ffffffffffffffffffffffffffffffffffffffff8516611b5757611c75565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f08686604051602401611b869190612925565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611c0f91906125d0565b6000604051808303818686fa925050503d8060008114611c4b576040519150601f19603f3d011682016040523d82523d6000602084013e611c50565b606091505b5090925090508115611c735780806020019051611c709190810190612544565b92505b505b935093915050565b7374134cf88b21383713e096a5ecf59e297dc7f54790565b60008160140183511015611cbb57611cbb611cb66004855185601401611e59565b611efe565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce56700000000000000000000000000000000000000000000000000000000815250604051611d3e91906125d0565b600060405180830381855afa9150503d8060008114611d79576040519150601f19603f3d011682016040523d82523d6000602084013e611d7e565b606091505b5091509150818015611d91575080516020145b15611da457611da1816000611f06565b92505b5050919050565b600082821115611dc457611dc4611cb660028585611f12565b50900390565b600082611dd957506000611a42565b82820282848281611de657fe5b041461198d5761198d611cb660018686611f12565b60008282018381101561198d5761198d611cb660008686611f12565b600081611e2d57611e2d611cb660038585611f12565b6000828481611e3857fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b6060632800659560e01b848484604051602401611e7893929190612748565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b600061198d8383611f31565b606063e946c1bb60e01b848484604051602401611e7893929190612726565b60008160200183511015611f5257611f52611cb66005855185602001611e59565b50016020015190565b6040805160608101909152806000815260006020820181905260409091015290565b8035611a42816129a5565b600082601f830112611f98578081fd5b8135611fab611fa682612955565b61292e565b8181529150602080830190840160005b83811015611fe857611fd3876020843589010161205b565b83526020928301929190910190600101611fbb565b5050505092915050565b600082601f830112612002578081fd5b8135612010611fa682612955565b81815291506020808301908481018184028601820187101561203157600080fd5b60005b8481101561205057813584529282019290820190600101612034565b505050505092915050565b600082601f83011261206b578081fd5b813567ffffffffffffffff811115612081578182fd5b6120b260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161292e565b91508082528360208285010111156120c957600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b8114611a4257600080fd5b60006101c0808385031215612107578182fd5b6121108161292e565b91505061211d8383611f7d565b815261212c8360208401611f7d565b602082015261213e8360408401611f7d565b60408201526121508360608401611f7d565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff808211156121b257600080fd5b6121be8683870161205b565b838501526101609250828501359150808211156121da57600080fd5b6121e68683870161205b565b8385015261018092508285013591508082111561220257600080fd5b61220e8683870161205b565b838501526101a092508285013591508082111561222a57600080fd5b506122378582860161205b565b82840152505092915050565b600060208284031215612254578081fd5b815161198d816129a5565b600080600060608486031215612273578182fd5b833561227e816129a5565b9250602084013561228e816129a5565b9150604084013561229e816129a5565b809150509250925092565b600080600080608085870312156122be578081fd5b84356122c9816129a5565b935060208501356122d9816129a5565b925060408501356122e9816129a5565b9150606085013567ffffffffffffffff811115612304578182fd5b61231087828801611ff2565b91505092959194509250565b600080600060608486031215612330578283fd5b833561233b816129a5565b9250602084013561234b816129a5565b9150604084013567ffffffffffffffff811115612366578182fd5b61237286828701611ff2565b9150509250925092565b60008060008060808587031215612391578182fd5b843561239c816129a5565b93506123ab86602087016120e2565b92506122e986604087016120e2565b600080602083850312156123cc578182fd5b823567ffffffffffffffff808211156123e3578384fd5b81850186601f8201126123f4578485fd5b8035925081831115612404578485fd5b8660208085028301011115612417578485fd5b60200196919550909350505050565b60008060408385031215612438578182fd5b823567ffffffffffffffff8082111561244f578384fd5b81850186601f820112612460578485fd5b80359250612470611fa684612955565b83815260208082019190838101885b878110156124a8576124968c8484358901016120f4565b8552938201939082019060010161247f565b509197508801359450505050808211156124c0578283fd5b506124cd85828601611f88565b9150509250929050565b600080600083850360a08112156124ec578182fd5b60608112156124f9578182fd5b50612504606061292e565b845160078110612512578283fd5b81526020858101519082015260408086015190820152606085015160808601519194509250801515811461229e578182fd5b600060208284031215612555578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845261258e816020860160208601612975565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b600082516125e2818460208701612975565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b6000602080830181845280855180835260408601915060408482028701019250838701855b828110156126d6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526126c4858351612576565b9450928501929085019060010161268a565b5092979650505050505050565b602080825282518282018190526000918401906040840190835b8181101561271b5783518352602093840193909201916001016126fd565b509095945050505050565b606081016004851061273457fe5b938152602081019290925260409091015290565b606081016008851061273457fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b6000604082526127e560408301855161255c565b60208401516127f7606084018261255c565b50604084015161280a608084018261255c565b50606084015161281d60a084018261255c565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c09150610180828187015261288e610200870185612576565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a08388830301818901526128cd8287612576565b838b015196508489820301868a01526128e68188612576565b955050808a0151955050505080858303016101e0860152506129088183612576565b848103602086015261291a8187612576565b979650505050505050565b90815260200190565b60405181810167ffffffffffffffff8111828210171561294d57600080fd5b604052919050565b600067ffffffffffffffff82111561296b578081fd5b5060209081020190565b60005b83811015612990578181015183820152602001612978565b8381111561299f576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff811681146129c757600080fd5b5056fea365627a7a7231582034e7eab64809a5ab7420c524f487ac0ca03a80f2b355b2d12a392dde2df0522e6c6578706572696d656e74616cf564736f6c63430005100040" + "object": "0x608060405234801561001057600080fd5b50600436106100df5760003560e01c806364ee6ade1161008c5780638b123a02116100665780638b123a02146101b25780639f76ad35146101c5578063a2c28d4b146101d8578063c7f7142e146101eb576100df565b806364ee6ade1461016c57806368be3cf21461017f5780636dd6b78d1461019f576100df565b80634cb8e253116100bd5780634cb8e2531461013357806359f515d01461014657806360ee052a14610159576100df565b80631796fb87146100e4578063354152a31461010d5780634703a7e614610120575b600080fd5b6100f76100f236600461254d565b61020b565b60405161010491906128b4565b60405180910390f35b6100f761011b36600461254d565b6103dc565b6100f761012e3660046124ed565b610596565b6100f76101413660046124ed565b610772565b6100f76101543660046125f7565b610a62565b6100f76101673660046124ed565b610b0b565b6100f761017a3660046124ed565b610dcb565b61019261018d36600461258b565b610f91565b6040516101049190612836565b6100f76101ad3660046124ed565b6110d0565b6100f76101c03660046125f7565b611378565b6100f76101d336600461247a565b611651565b6100f76101e636600461247a565b611856565b6101fe6101f9366004612430565b611a42565b60405161010491906127bd565b60606000825190508060405190808252806020026020018201604052801561023d578160200160208202803883390190505b50915060005b818110156103d257600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff16630e71d1b9905060e01b8a8a8a888151811061029a57fe5b60200260200101516040516024016102b493929190612927565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161033d91906127a1565b6000604051808303818686fa925050503d8060008114610379576040519150601f19603f3d011682016040523d82523d6000602084013e61037e565b606091505b509092509050600082156103a757818060200190516103a09190810190612715565b90506103af565b5050506103d2565b808685815181106103bc57fe5b6020908102919091010152505050600101610243565b5050949350505050565b60606000825190508060405190808252806020026020018201604052801561040e578160200160208202803883390190505b50915060005b818110156103d257600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a888151811061046b57fe5b602002602001015160405160240161048593929190612927565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161050e91906127a1565b6000604051808303818686fa925050503d806000811461054a576040519150601f19603f3d011682016040523d82523d6000602084013e61054f565b606091505b509092509050600082156103a757818060200190516105719190810190612715565b90508086858151811061058057fe5b6020908102919091010152505050600101610414565b60606105a28385611b7d565b81516040805182815260208084028201019091528180156105cd578160200160208202803883390190505b50915060005b8181101561076957600060606105e7611bf0565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a888151811061063157fe5b602002602001015160405160240161064b93929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516106d491906127a1565b6000604051808303818686fa925050503d8060008114610710576040519150601f19603f3d011682016040523d82523d6000602084013e610715565b606091505b5090925090506000821561073e57818060200190516107379190810190612715565b9050610746565b505050610769565b8086858151811061075357fe5b60209081029190910101525050506001016105d3565b50509392505050565b606061077e8385611b7d565b6000610788611c08565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146107c057846107d6565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006107e2611c08565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161461081a5784610830565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b9050600061083d87611c20565b60ff169050600061084d87611c20565b60ff16905060008651905080604051908082528060200260200182016040528015610882578160200160208202803883390190505b50955060005b81811015610a55576000606061089c611c31565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b8a8a8e88815181106108e657fe5b602002602001015160405160240161090093929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161098991906127a1565b6000604051808303818686fa925050503d80600081146109c5576040519150601f19603f3d011682016040523d82523d6000602084013e6109ca565b606091505b509092509050600082156109f357818060200190516109ec9190810190612715565b90506109fb565b505050610a55565b670de0b6b3a764000087600a0a87600a0a8d8781518110610a1857fe5b602002602001015184020281610a2a57fe5b0481610a3257fe5b048a8581518110610a3f57fe5b6020908102919091010152505050600101610888565b5050505050509392505050565b6060610a6e8383611378565b905060005b8351811015610b0457818181518110610a8857fe5b6020026020010151600014610afc57610ae3828281518110610aa657fe5b6020026020010151858381518110610aba57fe5b602002602001015160a00151868481518110610ad257fe5b602002602001015160800151611c49565b828281518110610aef57fe5b6020026020010181815250505b600101610a73565b5092915050565b6060610b178385611b7d565b8151604080518281526020808402820101909152818015610b42578160200160208202803883390190505b5091506000610b4f611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610b8f57610b8a86611c8b565b610b92565b60005b90506000610b9e611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610bde57610bd986611c8b565b610be1565b60005b905060005b83811015610dc0576001610bf8611c08565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415610c8f578651610c6e9085907f2640f62c00000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b6020026020010151611d1d565b878481518110610c7a57fe5b60200260200101819350828152505050610dac565b610c97611c08565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415610d00578651610c6e9084907f59e9486200000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b8651600090610d399085907f59e9486200000000000000000000000000000000000000000000000000000000908b9087908110610c6157fe5b925090508015610d8f57610d6e857f2640f62c0000000000000000000000000000000000000000000000000000000083611d1d565b888581518110610d7a57fe5b60200260200101819450828152505050610daa565b6000878481518110610d9d57fe5b6020026020010181815250505b505b80610db75750610dc0565b50600101610be6565b505050509392505050565b6060610dd78385611b7d565b8151604080518281526020808402820101909152818015610e02578160200160208202803883390190505b50915060005b818110156107695760006060610e1c611bf0565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a8881518110610e6657fe5b6020026020010151604051602401610e8093929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610f0991906127a1565b6000604051808303818686fa925050503d8060008114610f45576040519150601f19603f3d011682016040523d82523d6000602084013e610f4a565b606091505b5090925090506000821561073e5781806020019051610f6c9190810190612715565b905080868581518110610f7b57fe5b6020908102919091010152505050600101610e08565b604080518281526020808402820101909152606090828015610fc757816020015b6060815260200190600190039081610fb25790505b50905060005b808314610b04576000606030868685818110610fe557fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13684900301811261101f57600080fd5b9091016020810191503567ffffffffffffffff81111561103e57600080fd5b3681900382131561104e57600080fd5b60405161105c929190612791565b600060405180830381855afa9150503d8060008114611097576040519150601f19603f3d011682016040523d82523d6000602084013e61109c565b606091505b5091509150816110ae57805160208201fd5b808484815181106110bb57fe5b60209081029190910101525050600101610fcd565b60606110dc8385611b7d565b8151604080518281526020808402820101909152818015611107578160200160208202803883390190505b5091506000611114611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146111545761114f86611c8b565b611157565b60005b90506000611163611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146111a35761119e86611c8b565b6111a6565b60005b905060005b83811015610dc05760016111bd611c08565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614156112475786516112269085907f95b68fe700000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b87848151811061123257fe5b60200260200101819350828152505050611364565b61124f611c08565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156112b85786516112269084907fcd7724c300000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b86516000906112f19086907f95b68fe700000000000000000000000000000000000000000000000000000000908b9087908110610c6157fe5b92509050801561134757611326847fcd7724c30000000000000000000000000000000000000000000000000000000083611d1d565b88858151811061133257fe5b60200260200101819450828152505050611362565b600087848151811061135557fe5b6020026020010181815250505b505b8061136f5750610dc0565b506001016111ab565b606082516040519080825280602002602001820160405280156113a5578160200160208202803883390190505b506000805491925073ffffffffffffffffffffffffffffffffffffffff909116905b84518114611649578381815181106113db57fe5b6020026020010151516000148061140957508481815181106113f957fe5b6020026020010151608001516000145b8061142b575084818151811061141b57fe5b602002602001015160a001516000145b1561144f57600083828151811061143e57fe5b602002602001018181525050611641565b600060608373ffffffffffffffffffffffffffffffffffffffff166207a1208573ffffffffffffffffffffffffffffffffffffffff1663e77286eb905060e01b89868151811061149b57fe5b60200260200101518987815181106114af57fe5b60200260200101516040516024016114c89291906129a2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161155191906127a1565b6000604051808303818686fa925050503d806000811461158d576040519150601f19603f3d011682016040523d82523d6000602084013e611592565b606091505b5091509150816115bd5760008584815181106115aa57fe5b6020026020010181815250505050611641565b6115c561212c565b600080838060200190516115dc91908101906126a8565b919450925090506003835160068111156115f257fe5b1415806115fd575080155b1561162157600088878151811061161057fe5b60200260200101818152505061163b565b8188878151811061162e57fe5b6020026020010181815250505b50505050505b6001016113c7565b505092915050565b606060008251905080604051908082528060200260200182016040528015611683578160200160208202803883390190505b5091506000611693878787611a42565b905073ffffffffffffffffffffffffffffffffffffffff81166116b8575061184e9050565b60005b8281101561184a57600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b888151811061171257fe5b602002602001015160405160240161172c93929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516117b591906127a1565b6000604051808303818686fa925050503d80600081146117f1576040519150601f19603f3d011682016040523d82523d6000602084013e6117f6565b606091505b5090925090506000821561181f57818060200190516118189190810190612715565b9050611827565b50505061184a565b8087858151811061183457fe5b60209081029190910101525050506001016116bb565b5050505b949350505050565b606060008251905080604051908082528060200260200182016040528015611888578160200160208202803883390190505b5091506000611898878787611a42565b905073ffffffffffffffffffffffffffffffffffffffff81166118bd575061184e9050565b60005b8281101561184a57600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff166345060eb0905060e01b8b8b8b888151811061191757fe5b602002602001015160405160240161193193929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516119ba91906127a1565b6000604051808303818686fa925050503d80600081146119f6576040519150601f19603f3d011682016040523d82523d6000602084013e6119fb565b606091505b5090925090506000821561181f5781806020019051611a1d9190810190612715565b905080878581518110611a2c57fe5b60209081029190910101525050506001016118c0565b6040516000906060907f153f59970000000000000000000000000000000000000000000000000000000090611a7d90869086906024016127de565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff1683604051611b0591906127a1565b600060405180830381855afa9150503d8060008114611b40576040519150601f19603f3d011682016040523d82523d6000602084013e611b45565b606091505b5091509150818015611b58575080516020145b15611b7257611b6881600c611e66565b9350505050611b76565b5050505b9392505050565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611bec576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611be390612945565b60405180910390fd5b5050565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b6000611c2b82611eab565b92915050565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b600061184e83611c7f611c6382600163ffffffff611f7c16565b611c73888763ffffffff611f9b16565b9063ffffffff611fcc16565b9063ffffffff611fe816565b6000611c95612012565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b8152600401611ccd91906127bd565b60206040518083038186803b158015611ce557600080fd5b505afa158015611cf9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c2b9190810190612414565b60008073ffffffffffffffffffffffffffffffffffffffff8516611d4057611e5e565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f08686604051602401611d6f9190612af6565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611df891906127a1565b6000604051808303818686fa925050503d8060008114611e34576040519150601f19603f3d011682016040523d82523d6000602084013e611e39565b606091505b5090925090508115611e5c5780806020019051611e599190810190612715565b92505b505b935093915050565b60008160140183511015611e8c57611e8c611e87600485518560140161202a565b6120cf565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce56700000000000000000000000000000000000000000000000000000000815250604051611f0f91906127a1565b600060405180830381855afa9150503d8060008114611f4a576040519150601f19603f3d011682016040523d82523d6000602084013e611f4f565b606091505b5091509150818015611f62575080516020145b15611f7557611f728160006120d7565b92505b5050919050565b600082821115611f9557611f95611e87600285856120e3565b50900390565b600082611faa57506000611c2b565b82820282848281611fb757fe5b0414611b7657611b76611e87600186866120e3565b600082820183811015611b7657611b76611e87600086866120e3565b600081611ffe57611ffe611e87600385856120e3565b600082848161200957fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b6060632800659560e01b84848460405160240161204993929190612919565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b6000611b768383612102565b606063e946c1bb60e01b848484604051602401612049939291906128f7565b6000816020018351101561212357612123611e87600585518560200161202a565b50016020015190565b6040805160608101909152806000815260006020820181905260409091015290565b8035611c2b81612b76565b600082601f830112612169578081fd5b813561217c61217782612b26565b612aff565b8181529150602080830190840160005b838110156121b9576121a4876020843589010161222c565b8352602092830192919091019060010161218c565b5050505092915050565b600082601f8301126121d3578081fd5b81356121e161217782612b26565b81815291506020808301908481018184028601820187101561220257600080fd5b60005b8481101561222157813584529282019290820190600101612205565b505050505092915050565b600082601f83011261223c578081fd5b813567ffffffffffffffff811115612252578182fd5b61228360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601612aff565b915080825283602082850101111561229a57600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b8114611c2b57600080fd5b60006101c08083850312156122d8578182fd5b6122e181612aff565b9150506122ee838361214e565b81526122fd836020840161214e565b602082015261230f836040840161214e565b6040820152612321836060840161214e565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff8082111561238357600080fd5b61238f8683870161222c565b838501526101609250828501359150808211156123ab57600080fd5b6123b78683870161222c565b838501526101809250828501359150808211156123d357600080fd5b6123df8683870161222c565b838501526101a09250828501359150808211156123fb57600080fd5b506124088582860161222c565b82840152505092915050565b600060208284031215612425578081fd5b8151611b7681612b76565b600080600060608486031215612444578182fd5b833561244f81612b76565b9250602084013561245f81612b76565b9150604084013561246f81612b76565b809150509250925092565b6000806000806080858703121561248f578081fd5b843561249a81612b76565b935060208501356124aa81612b76565b925060408501356124ba81612b76565b9150606085013567ffffffffffffffff8111156124d5578182fd5b6124e1878288016121c3565b91505092959194509250565b600080600060608486031215612501578283fd5b833561250c81612b76565b9250602084013561251c81612b76565b9150604084013567ffffffffffffffff811115612537578182fd5b612543868287016121c3565b9150509250925092565b60008060008060808587031215612562578182fd5b843561256d81612b76565b935061257c86602087016122b3565b92506124ba86604087016122b3565b6000806020838503121561259d578182fd5b823567ffffffffffffffff808211156125b4578384fd5b81850186601f8201126125c5578485fd5b80359250818311156125d5578485fd5b86602080850283010111156125e8578485fd5b60200196919550909350505050565b60008060408385031215612609578182fd5b823567ffffffffffffffff80821115612620578384fd5b81850186601f820112612631578485fd5b8035925061264161217784612b26565b83815260208082019190838101885b87811015612679576126678c8484358901016122c5565b85529382019390820190600101612650565b50919750880135945050505080821115612691578283fd5b5061269e85828601612159565b9150509250929050565b600080600083850360a08112156126bd578182fd5b60608112156126ca578182fd5b506126d56060612aff565b8451600781106126e3578283fd5b81526020858101519082015260408086015190820152606085015160808601519194509250801515811461246f578182fd5b600060208284031215612726578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845261275f816020860160208601612b46565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b600082516127b3818460208701612b46565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b6000602080830181845280855180835260408601915060408482028701019250838701855b828110156128a7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452612895858351612747565b9450928501929085019060010161285b565b5092979650505050505050565b602080825282518282018190526000918401906040840190835b818110156128ec5783518352602093840193909201916001016128ce565b509095945050505050565b606081016004851061290557fe5b938152602081019290925260409091015290565b606081016008851061290557fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b6000604082526129b660408301855161272d565b60208401516129c8606084018261272d565b5060408401516129db608084018261272d565b5060608401516129ee60a084018261272d565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c091506101808281870152612a5f610200870185612747565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a0838883030181890152612a9e8287612747565b838b015196508489820301868a0152612ab78188612747565b955050808a0151955050505080858303016101e086015250612ad98183612747565b8481036020860152612aeb8187612747565b979650505050505050565b90815260200190565b60405181810167ffffffffffffffff81118282101715612b1e57600080fd5b604052919050565b600067ffffffffffffffff821115612b3c578081fd5b5060209081020190565b60005b83811015612b61578181015183820152602001612b49565b83811115612b70576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff81168114612b9857600080fd5b5056fea365627a7a72315820fd16ce5ab5cf6ae3213442d55793e044c4eb1e0d181b86e7dc7964a0e1a416f86c6578706572696d656e74616cf564736f6c63430005110040" } } }, "compiler": { "name": "solc", - "version": "soljson-v0.5.16+commit.9c3226ce.js", + "version": "0.5.17+commit.d19bba13", "settings": { "optimizer": { "enabled": true, diff --git a/packages/contract-artifacts/artifacts/IERC20BridgeSampler.json b/packages/contract-artifacts/artifacts/IERC20BridgeSampler.json index a851bba79c..e82d77ab4a 100644 --- a/packages/contract-artifacts/artifacts/IERC20BridgeSampler.json +++ b/packages/contract-artifacts/artifacts/IERC20BridgeSampler.json @@ -93,6 +93,20 @@ "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [ + { "internalType": "address", "name": "curveAddress", "type": "address" }, + { "internalType": "int128", "name": "fromTokenIdx", "type": "int128" }, + { "internalType": "int128", "name": "toTokenIdx", "type": "int128" }, + { "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" } + ], + "name": "sampleBuysFromCurve", + "outputs": [{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [ @@ -232,12 +246,22 @@ }, "return": "orderFillableTakerAssetAmounts How much taker asset can be filled by each order in `orders`." }, + "sampleBuysFromCurve(address,int128,int128,uint256[])": { + "details": "Sample buy quotes from Curve.", + "params": { + "curveAddress": "Address of the Curve contract.", + "fromTokenIdx": "Index of the taker token (what to sell).", + "makerTokenAmounts": "Maker token buy amount for each sample.", + "toTokenIdx": "Index of the maker token (what to buy)." + }, + "return": "takerTokenAmounts Taker amounts sold at each maker token amount." + }, "sampleBuysFromEth2Dai(address,address,uint256[])": { "details": "Sample buy quotes from Eth2Dai/Oasis.", "params": { "makerToken": "Address of the maker token (what to buy).", - "takerToken": "Address of the taker token (what to sell).", - "takerTokenAmounts": "Maker token sell amount for each sample." + "makerTokenAmounts": "Maker token buy amount for each sample.", + "takerToken": "Address of the taker token (what to sell)." }, "return": "takerTokenAmounts Taker amounts sold at each maker token amount." }, @@ -255,7 +279,7 @@ "details": "Sample buy quotes from Uniswap.", "params": { "makerToken": "Address of the maker token (what to buy).", - "makerTokenAmounts": "Maker token sell amount for each sample.", + "makerTokenAmounts": "Maker token buy amount for each sample.", "takerToken": "Address of the taker token (what to sell)." }, "return": "takerTokenAmounts Taker amounts sold at each maker token amount." @@ -313,7 +337,7 @@ }, "compiler": { "name": "solc", - "version": "soljson-v0.5.16+commit.9c3226ce.js", + "version": "0.5.17+commit.d19bba13", "settings": { "optimizer": { "enabled": true, diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index 0741c46114..22144e0b0f 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -13,6 +13,9 @@ { "note": "Added `Forwarder.marketSellAmountWithEth`", "pr": 2521 + }, + { + "note": "Added `ERC20BridgeSampler.sampleBuysFromCurve`" } ] }, diff --git a/packages/contract-wrappers/src/generated-wrappers/erc20_bridge_sampler.ts b/packages/contract-wrappers/src/generated-wrappers/erc20_bridge_sampler.ts index c7ae4fee03..efbf89f3c9 100644 --- a/packages/contract-wrappers/src/generated-wrappers/erc20_bridge_sampler.ts +++ b/packages/contract-wrappers/src/generated-wrappers/erc20_bridge_sampler.ts @@ -49,6 +49,7 @@ export class ERC20BridgeSamplerContract extends BaseContract { supportedProvider: SupportedProvider, txDefaults: Partial, logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact }, + devUtilsAddress: string, ): Promise { assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [ schemas.addressSchema, @@ -73,6 +74,7 @@ export class ERC20BridgeSamplerContract extends BaseContract { provider, txDefaults, logDecodeDependenciesAbiOnly, + devUtilsAddress, ); } @@ -82,6 +84,7 @@ export class ERC20BridgeSamplerContract extends BaseContract { supportedProvider: SupportedProvider, txDefaults: Partial, logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact }, + devUtilsAddress: string, ): Promise { assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [ schemas.addressSchema, @@ -112,6 +115,7 @@ export class ERC20BridgeSamplerContract extends BaseContract { provider, txDefaults, logDecodeDependenciesAbiOnly, + devUtilsAddress, ); } @@ -121,6 +125,7 @@ export class ERC20BridgeSamplerContract extends BaseContract { supportedProvider: SupportedProvider, txDefaults: Partial, logDecodeDependencies: { [contractName: string]: ContractAbi }, + devUtilsAddress: string, ): Promise { assert.isHexString('bytecode', bytecode); assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [ @@ -130,10 +135,14 @@ export class ERC20BridgeSamplerContract extends BaseContract { ]); const provider = providerUtils.standardizeOrThrow(supportedProvider); const constructorAbi = BaseContract._lookupConstructorAbi(abi); - [] = BaseContract._formatABIDataItemList(constructorAbi.inputs, [], BaseContract._bigNumberToString); + [devUtilsAddress] = BaseContract._formatABIDataItemList( + constructorAbi.inputs, + [devUtilsAddress], + BaseContract._bigNumberToString, + ); const iface = new ethers.utils.Interface(abi); const deployInfo = iface.deployFunction; - const txData = deployInfo.encode(bytecode, []); + const txData = deployInfo.encode(bytecode, [devUtilsAddress]); const web3Wrapper = new Web3Wrapper(provider); const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync( { @@ -152,7 +161,7 @@ export class ERC20BridgeSamplerContract extends BaseContract { txDefaults, logDecodeDependencies, ); - contractInstance.constructorArgs = []; + contractInstance.constructorArgs = [devUtilsAddress]; return contractInstance; } @@ -161,6 +170,18 @@ export class ERC20BridgeSamplerContract extends BaseContract { */ public static ABI(): ContractAbi { const abi = [ + { + inputs: [ + { + name: 'devUtilsAddress', + type: 'address', + }, + ], + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'constructor', + }, { constant: true, inputs: [ @@ -369,6 +390,37 @@ export class ERC20BridgeSamplerContract extends BaseContract { stateMutability: 'view', type: 'function', }, + { + constant: true, + inputs: [ + { + name: 'curveAddress', + type: 'address', + }, + { + name: 'fromTokenIdx', + type: 'int128', + }, + { + name: 'toTokenIdx', + type: 'int128', + }, + { + name: 'makerTokenAmounts', + type: 'uint256[]', + }, + ], + name: 'sampleBuysFromCurve', + outputs: [ + { + name: 'takerTokenAmounts', + type: 'uint256[]', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, { constant: true, inputs: [ @@ -838,6 +890,48 @@ export class ERC20BridgeSamplerContract extends BaseContract { }, }; } + /** + * Sample buy quotes from Curve. + * @param curveAddress Address of the Curve contract. + * @param fromTokenIdx Index of the taker token (what to sell). + * @param toTokenIdx Index of the maker token (what to buy). + * @param makerTokenAmounts Maker token buy amount for each sample. + * @returns takerTokenAmounts Taker amounts sold at each maker token amount. + */ + public sampleBuysFromCurve( + curveAddress: string, + fromTokenIdx: BigNumber, + toTokenIdx: BigNumber, + makerTokenAmounts: BigNumber[], + ): ContractFunctionObj { + const self = (this as any) as ERC20BridgeSamplerContract; + assert.isString('curveAddress', curveAddress); + assert.isBigNumber('fromTokenIdx', fromTokenIdx); + assert.isBigNumber('toTokenIdx', toTokenIdx); + assert.isArray('makerTokenAmounts', makerTokenAmounts); + const functionSignature = 'sampleBuysFromCurve(address,int128,int128,uint256[])'; + + return { + async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { + BaseContract._assertCallParams(callData, defaultBlock); + const rawCallResult = await self._performCallAsync( + { ...callData, data: this.getABIEncodedTransactionData() }, + defaultBlock, + ); + const abiEncoder = self._lookupAbiEncoder(functionSignature); + BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder); + return abiEncoder.strictDecodeReturnValue(rawCallResult); + }, + getABIEncodedTransactionData(): string { + return self._strictEncodeArguments(functionSignature, [ + curveAddress.toLowerCase(), + fromTokenIdx, + toTokenIdx, + makerTokenAmounts, + ]); + }, + }; + } /** * Sample buy quotes from Eth2Dai/Oasis. * @param takerToken Address of the taker token (what to sell). diff --git a/packages/contract-wrappers/src/generated-wrappers/i_erc20_bridge_sampler.ts b/packages/contract-wrappers/src/generated-wrappers/i_erc20_bridge_sampler.ts index 612c63263b..39cb69e78f 100644 --- a/packages/contract-wrappers/src/generated-wrappers/i_erc20_bridge_sampler.ts +++ b/packages/contract-wrappers/src/generated-wrappers/i_erc20_bridge_sampler.ts @@ -369,6 +369,37 @@ export class IERC20BridgeSamplerContract extends BaseContract { stateMutability: 'view', type: 'function', }, + { + constant: true, + inputs: [ + { + name: 'curveAddress', + type: 'address', + }, + { + name: 'fromTokenIdx', + type: 'int128', + }, + { + name: 'toTokenIdx', + type: 'int128', + }, + { + name: 'makerTokenAmounts', + type: 'uint256[]', + }, + ], + name: 'sampleBuysFromCurve', + outputs: [ + { + name: 'takerTokenAmounts', + type: 'uint256[]', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, { constant: true, inputs: [ @@ -835,10 +866,53 @@ export class IERC20BridgeSamplerContract extends BaseContract { }, }; } + /** + * Sample buy quotes from Curve. + * @param curveAddress Address of the Curve contract. + * @param fromTokenIdx Index of the taker token (what to sell). + * @param toTokenIdx Index of the maker token (what to buy). + * @param makerTokenAmounts Maker token buy amount for each sample. + * @returns takerTokenAmounts Taker amounts sold at each maker token amount. + */ + public sampleBuysFromCurve( + curveAddress: string, + fromTokenIdx: BigNumber, + toTokenIdx: BigNumber, + makerTokenAmounts: BigNumber[], + ): ContractFunctionObj { + const self = (this as any) as IERC20BridgeSamplerContract; + assert.isString('curveAddress', curveAddress); + assert.isBigNumber('fromTokenIdx', fromTokenIdx); + assert.isBigNumber('toTokenIdx', toTokenIdx); + assert.isArray('makerTokenAmounts', makerTokenAmounts); + const functionSignature = 'sampleBuysFromCurve(address,int128,int128,uint256[])'; + + return { + async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { + BaseContract._assertCallParams(callData, defaultBlock); + const rawCallResult = await self._performCallAsync( + { ...callData, data: this.getABIEncodedTransactionData() }, + defaultBlock, + ); + const abiEncoder = self._lookupAbiEncoder(functionSignature); + BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder); + return abiEncoder.strictDecodeReturnValue(rawCallResult); + }, + getABIEncodedTransactionData(): string { + return self._strictEncodeArguments(functionSignature, [ + curveAddress.toLowerCase(), + fromTokenIdx, + toTokenIdx, + makerTokenAmounts, + ]); + }, + }; + } /** * Sample buy quotes from Eth2Dai/Oasis. * @param takerToken Address of the taker token (what to sell). * @param makerToken Address of the maker token (what to buy). + * @param makerTokenAmounts Maker token buy amount for each sample. * @returns takerTokenAmounts Taker amounts sold at each maker token amount. */ public sampleBuysFromEth2Dai( @@ -918,7 +992,7 @@ export class IERC20BridgeSamplerContract extends BaseContract { * Sample buy quotes from Uniswap. * @param takerToken Address of the taker token (what to sell). * @param makerToken Address of the maker token (what to buy). - * @param makerTokenAmounts Maker token sell amount for each sample. + * @param makerTokenAmounts Maker token buy amount for each sample. * @returns takerTokenAmounts Taker amounts sold at each maker token amount. */ public sampleBuysFromUniswap( From e9eb9a40b08cce8893b0c929232baabe6124b67b Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Thu, 16 Apr 2020 15:11:00 +1000 Subject: [PATCH 02/11] Fake Buy Kyber/PLP --- .../contracts/src/ERC20BridgeSampler.sol | 205 ++++++++++++++++-- .../test/erc20-bridge-sampler.ts | 101 ++++++++- .../bridge_sampler_mainnet_test.ts | 95 +++++--- .../utils/market_operation_utils/sampler.ts | 4 +- .../sampler_operations.ts | 6 +- .../src/utils/quote_simulation.ts | 4 +- 6 files changed, 357 insertions(+), 58 deletions(-) diff --git a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol index ba2a3826ab..8a3626ca19 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol @@ -213,6 +213,127 @@ contract ERC20BridgeSampler is } } + struct FakeBuyContext { + uint256 buyAmount; + uint256 sellAmount; + uint256 rate; + uint256 slippageFromTarget; + } + + /// @dev Sample buy quotes from Kyber. + /// @param takerToken Address of the taker token (what to sell). + /// @param makerToken Address of the maker token (what to buy). + /// @param makerTokenAmounts Maker token buy amount for each sample. + /// @return takerTokenAmounts Taker amounts sold at each maker token + /// amount. + function sampleBuysFromKyberNetwork( + address takerToken, + address makerToken, + uint256[] memory makerTokenAmounts + ) + public + view + returns (uint256[] memory takerTokenAmounts) + { + _assertValidPair(makerToken, takerToken); + if (makerTokenAmounts.length == 0) { + return takerTokenAmounts; + } + address _takerToken = takerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : takerToken; + address _makerToken = makerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : makerToken; + uint256 takerTokenDecimals = _getTokenDecimals(takerToken); + uint256 makerTokenDecimals = _getTokenDecimals(makerToken); + takerTokenAmounts = new uint256[](makerTokenAmounts.length); + FakeBuyContext memory context; + // Quote the first amount selling the makerTokenAmount + (bool didSucceed, bytes memory resultData) = + _getKyberNetworkProxyAddress().staticcall.gas(KYBER_CALL_GAS)( + abi.encodeWithSelector( + IKyberNetwork(0).getExpectedRate.selector, + _makerToken, + _takerToken, + makerTokenAmounts[0] + )); + if (didSucceed) { + context.rate = abi.decode(resultData, (uint256)); + } else { + return takerTokenAmounts; + } + // Calculate the initial sell amount from the rate and the maker token sold + context.sellAmount = context.rate * makerTokenAmounts[0] * + 10 ** takerTokenDecimals / + 10 ** makerTokenDecimals / + 10 ** 18; + + (didSucceed, resultData) = + _getKyberNetworkProxyAddress().staticcall.gas(KYBER_CALL_GAS)( + abi.encodeWithSelector( + IKyberNetwork(0).getExpectedRate.selector, + _takerToken, + _makerToken, + context.sellAmount + )); + if (didSucceed) { + context.rate = abi.decode(resultData, (uint256)); + } else { + return takerTokenAmounts; + } + // Calculate the buy amount from the rate and the amount sold + context.buyAmount = context.rate * context.sellAmount * + 10 ** makerTokenDecimals / + 10 ** takerTokenDecimals / + 10 ** 18; + for (uint256 i = 0; i < makerTokenAmounts.length; i++) { + uint256 iteration = 0; + context.slippageFromTarget = 10; + do { + // adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER + context.sellAmount = LibMath.getPartialAmountCeil( + makerTokenAmounts[i], + context.buyAmount, + context.sellAmount + ); + // JUMP Multiplier by 1.0005 + context.sellAmount = LibMath.getPartialAmountCeil( + 10005, + 10000, + context.sellAmount + ); + + (didSucceed, resultData) = + _getKyberNetworkProxyAddress().staticcall.gas(KYBER_CALL_GAS)( + abi.encodeWithSelector( + IKyberNetwork(0).getExpectedRate.selector, + _takerToken, + _makerToken, + context.sellAmount + )); + if (didSucceed) { + context.rate = abi.decode(resultData, (uint256)); + } else { + break; + } + + context.buyAmount = context.rate * context.sellAmount * + 10 ** makerTokenDecimals / + 10 ** takerTokenDecimals / + 10 ** 18; + // 0.0005 slippage is the target + if (context.buyAmount >= makerTokenAmounts[i]) { + context.slippageFromTarget = (context.buyAmount - makerTokenAmounts[i]) / (makerTokenAmounts[i] / 10000); + } + } while ((context.buyAmount < makerTokenAmounts[i] && context.slippageFromTarget > 5) && ++iteration < 5); + // We do our best to close in on the requested amount, but we can either over buy or under buy and exit + // if we hit a max iteration limit + // We scale the sell amount to get the approximate target + takerTokenAmounts[i] = LibMath.getPartialAmountCeil( + makerTokenAmounts[i], + context.buyAmount, + context.sellAmount + ); + } + } + /// @dev Sample sell quotes from Eth2Dai/Oasis. /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). @@ -442,6 +563,7 @@ contract ERC20BridgeSampler is makerTokenAmounts[i] = buyAmount; } } + /// @dev Sample buy quotes from Curve. /// @param curveAddress Address of the Curve contract. /// @param fromTokenIdx Index of the taker token (what to sell). @@ -560,28 +682,83 @@ contract ERC20BridgeSampler is makerToken ); // If provider doesn't exist, return all zeros. - if (providerAddress == address(0)) { + if (providerAddress == address(0) || numSamples == 0) { return takerTokenAmounts; } - // Otherwise, query liquidity provider for quotes. - for (uint256 i = 0; i < numSamples; i++) { - (bool didSucceed, bytes memory resultData) = + FakeBuyContext memory context; + // Quote the first amount selling the makerTokenAmount + (bool didSucceed, bytes memory resultData) = + providerAddress.staticcall.gas(DEFAULT_CALL_GAS)( + abi.encodeWithSelector( + ILiquidityProvider(0).getSellQuote.selector, + makerToken, + takerToken, + makerTokenAmounts[0] + )); + if (didSucceed) { + context.sellAmount = abi.decode(resultData, (uint256)); + } else { + return takerTokenAmounts; + } + + (didSucceed, resultData) = providerAddress.staticcall.gas(DEFAULT_CALL_GAS)( abi.encodeWithSelector( - ILiquidityProvider(0).getBuyQuote.selector, + ILiquidityProvider(0).getSellQuote.selector, takerToken, makerToken, - makerTokenAmounts[i] + context.sellAmount )); - uint256 sellAmount = 0; - if (didSucceed) { - sellAmount = abi.decode(resultData, (uint256)); - } else { - // Exit early if the amount is too high for the liquidity provider to serve - break; - } - takerTokenAmounts[i] = sellAmount; + if (didSucceed) { + context.buyAmount = abi.decode(resultData, (uint256)); + } else { + return takerTokenAmounts; + } + + for (uint256 i = 0; i < makerTokenAmounts.length; i++) { + uint256 iteration = 0; + context.slippageFromTarget = 10; + do { + // adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER + context.sellAmount = LibMath.getPartialAmountCeil( + makerTokenAmounts[i], + context.buyAmount, + context.sellAmount + ); + // JUMP Multiplier by 1.0005 + context.sellAmount = LibMath.getPartialAmountCeil( + 10005, + 10000, + context.sellAmount + ); + + (didSucceed, resultData) = + providerAddress.staticcall.gas(DEFAULT_CALL_GAS)( + abi.encodeWithSelector( + ILiquidityProvider(0).getSellQuote.selector, + takerToken, + makerToken, + context.sellAmount + )); + if (didSucceed) { + context.buyAmount = abi.decode(resultData, (uint256)); + } else { + break; + } + // 0.0005 slippage is the target + if (context.buyAmount >= makerTokenAmounts[i]) { + context.slippageFromTarget = (context.buyAmount - makerTokenAmounts[i]) / (makerTokenAmounts[i] / 10000); + } + } while ((context.buyAmount < makerTokenAmounts[i] && context.slippageFromTarget > 5) && ++iteration < 5); + // We do our best to close in on the requested amount, but we can either over buy or under buy and exit + // if we hit a max iteration limit + // We scale the sell amount to get the approximate target + takerTokenAmounts[i] = LibMath.getPartialAmountCeil( + makerTokenAmounts[i], + context.buyAmount, + context.sellAmount + ); } } diff --git a/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts b/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts index a1d5bba154..1e59d3f9a2 100644 --- a/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts +++ b/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts @@ -169,8 +169,15 @@ blockchainTests('erc20-bridge-sampler', env => { for (const source of sources) { const sampleOutputs = []; for (const amount of sampleAmounts) { - if (source === 'Eth2Dai') { - sampleOutputs.push(getDeterministicBuyQuote(ETH2DAI_SALT, sellToken, buyToken, amount)); + if (source === 'Kyber' || source === 'Eth2Dai') { + sampleOutputs.push( + getDeterministicBuyQuote( + source === 'Kyber' ? KYBER_SALT : ETH2DAI_SALT, + sellToken, + buyToken, + amount, + ), + ); } else if (source === 'Uniswap') { sampleOutputs.push(getDeterministicUniswapBuyQuote(sellToken, buyToken, amount)); } @@ -204,7 +211,7 @@ blockchainTests('erc20-bridge-sampler', env => { function getSampleAmounts(tokenAddress: string, count?: number): BigNumber[] { const tokenDecimals = getDeterministicTokenDecimals(tokenAddress); - const _upperLimit = getRandomPortion(getRandomInteger(1, 1000).times(10 ** tokenDecimals)); + const _upperLimit = getRandomPortion(getRandomInteger(1000, 50000).times(10 ** tokenDecimals)); const _count = count || _.random(1, 16); const d = _upperLimit.div(_count); return _.times(_count, i => d.times((i + 1) / _count).integerValue()); @@ -388,6 +395,94 @@ blockchainTests('erc20-bridge-sampler', env => { }); }); + blockchainTests.resets('sampleBuysFromKyberNetwork()', () => { + const ACCEPTABLE_SLIPPAGE = 0.0005; + before(async () => { + await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync(); + }); + + it('throws if tokens are the same', async () => { + const tx = testContract.sampleBuysFromKyberNetwork(MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); + return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR); + }); + + it('can return no quotes', async () => { + const quotes = await testContract.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, []).callAsync(); + expect(quotes).to.deep.eq([]); + }); + const expectQuotesWithinRange = ( + quotes: BigNumber[], + expectedQuotes: BigNumber[], + maxSlippage: BigNumber | number, + ) => { + quotes.map((_q, i) => { + const slippage = quotes[i] + .dividedBy(expectedQuotes[i]) + .minus(1) + .decimalPlaces(4); + expect(slippage, `quote[${i}]: ${slippage}`).to.be.bignumber.gte(0); + expect(slippage, `quote[${i}] ${slippage}`).to.be.bignumber.lte(new BigNumber(maxSlippage)); + }); + }; + + it('can quote token - token', async () => { + const sampleAmounts = getSampleAmounts(TAKER_TOKEN); + const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts); + const quotes = await testContract + .sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .callAsync(); + expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); + }); + + it('returns zero if token -> token fails', async () => { + const sampleAmounts = getSampleAmounts(TAKER_TOKEN); + const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); + await enableFailTriggerAsync(); + const quotes = await testContract + .sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .callAsync(); + expect(quotes).to.deep.eq(expectedQuotes); + }); + + it('can quote token -> ETH', async () => { + const sampleAmounts = getSampleAmounts(TAKER_TOKEN); + const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts); + const quotes = await testContract + .sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .callAsync(); + expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); + }); + + it('returns zero if token -> ETH fails', async () => { + const sampleAmounts = getSampleAmounts(TAKER_TOKEN); + const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); + await enableFailTriggerAsync(); + const quotes = await testContract + .sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .callAsync(); + expect(quotes).to.deep.eq(expectedQuotes); + }); + + it('can quote ETH -> token', async () => { + const sampleAmounts = getSampleAmounts(TAKER_TOKEN); + const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts); + const quotes = await testContract + .sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .callAsync(); + expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); + }); + + it('returns zero if ETH -> token fails', async () => { + const sampleAmounts = getSampleAmounts(TAKER_TOKEN); + const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); + await enableFailTriggerAsync(); + const quotes = await testContract + .sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .callAsync(); + expect(quotes).to.deep.eq(expectedQuotes); + }); + }); + blockchainTests.resets('sampleSellsFromEth2Dai()', () => { before(async () => { await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync(); diff --git a/contracts/integrations/test/bridge_sampler/bridge_sampler_mainnet_test.ts b/contracts/integrations/test/bridge_sampler/bridge_sampler_mainnet_test.ts index 5781f52191..83d3ef2e3b 100644 --- a/contracts/integrations/test/bridge_sampler/bridge_sampler_mainnet_test.ts +++ b/contracts/integrations/test/bridge_sampler/bridge_sampler_mainnet_test.ts @@ -20,48 +20,75 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => { constants.NULL_ADDRESS, ); }); + describe('Curve', () => { + const CURVE_ADDRESS = '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56'; + const DAI_TOKEN_INDEX = new BigNumber(0); + const USDC_TOKEN_INDEX = new BigNumber(1); - const curveAddress = '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56'; - const daiTokenIdx = new BigNumber(0); - const usdcTokenIdx = new BigNumber(1); + describe('sampleSellsFromCurve()', () => { + it('samples sells from Curve DAI->USDC', async () => { + const samples = await testContract + .sampleSellsFromCurve(CURVE_ADDRESS, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1)]) + .callAsync(); + expect(samples.length).to.be.bignumber.greaterThan(0); + expect(samples[0]).to.be.bignumber.greaterThan(0); + }); - describe('sampleSellsFromCurve()', () => { - it('samples sells from Curve DAI->USDC', async () => { - const samples = await testContract - .sampleSellsFromCurve(curveAddress, daiTokenIdx, usdcTokenIdx, [toBaseUnitAmount(1)]) - .callAsync(); - expect(samples.length).to.be.bignumber.greaterThan(0); - expect(samples[0]).to.be.bignumber.greaterThan(0); + it('samples sells from Curve USDC->DAI', async () => { + const samples = await testContract + .sampleSellsFromCurve(CURVE_ADDRESS, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1, 6)]) + .callAsync(); + expect(samples.length).to.be.bignumber.greaterThan(0); + expect(samples[0]).to.be.bignumber.greaterThan(0); + }); }); - it('samples sells from Curve USDC->DAI', async () => { - const samples = await testContract - .sampleSellsFromCurve(curveAddress, usdcTokenIdx, daiTokenIdx, [toBaseUnitAmount(1, 6)]) - .callAsync(); - expect(samples.length).to.be.bignumber.greaterThan(0); - expect(samples[0]).to.be.bignumber.greaterThan(0); + describe('sampleBuysFromCurve()', () => { + it('samples buys from Curve DAI->USDC', async () => { + // From DAI to USDC + // I want to buy 1 USDC + const samples = await testContract + .sampleBuysFromCurve(CURVE_ADDRESS, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1, 6)]) + .callAsync(); + expect(samples.length).to.be.bignumber.greaterThan(0); + expect(samples[0]).to.be.bignumber.greaterThan(0); + }); + + it('samples buys from Curve USDC->DAI', async () => { + // From USDC to DAI + // I want to buy 1 DAI + const samples = await testContract + .sampleBuysFromCurve(CURVE_ADDRESS, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1)]) + .callAsync(); + expect(samples.length).to.be.bignumber.greaterThan(0); + expect(samples[0]).to.be.bignumber.greaterThan(0); + }); }); }); + describe('Kyber', () => { + const DAI_ADDRESS = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; + const WETH_ADDRESS = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - describe('sampleBuysFromCurve()', () => { - it('samples buys from Curve DAI->USDC', async () => { - // From DAI to USDC - // I want 1 to buy USDC - const samples = await testContract - .sampleBuysFromCurve(curveAddress, daiTokenIdx, usdcTokenIdx, [toBaseUnitAmount(1, 6)]) - .callAsync(); - expect(samples.length).to.be.bignumber.greaterThan(0); - expect(samples[0]).to.be.bignumber.greaterThan(0); - }); + describe('sampleBuysFromKyber()', () => { + it('samples buys from Kyber WETH->DAI', async () => { + // From ETH to DAI + // I want to buy 1 DAI + const samples = await testContract + .sampleBuysFromKyberNetwork(WETH_ADDRESS, DAI_ADDRESS, [toBaseUnitAmount(1)]) + .callAsync(); + expect(samples.length).to.be.bignumber.greaterThan(0); + expect(samples[0]).to.be.bignumber.greaterThan(0); + }); - it('samples buys from Curve USDC->DAI', async () => { - // From USDC to DAI - // I want 1 to buy 1 DAI - const samples = await testContract - .sampleBuysFromCurve(curveAddress, usdcTokenIdx, daiTokenIdx, [toBaseUnitAmount(1)]) - .callAsync(); - expect(samples.length).to.be.bignumber.greaterThan(0); - expect(samples[0]).to.be.bignumber.greaterThan(0); + it('samples buys from Kyber DAI->WETH', async () => { + // From USDC to DAI + // I want to buy 1 WETH + const samples = await testContract + .sampleBuysFromKyberNetwork(DAI_ADDRESS, WETH_ADDRESS, [toBaseUnitAmount(1)]) + .callAsync(); + expect(samples.length).to.be.bignumber.greaterThan(0); + expect(samples[0]).to.be.bignumber.greaterThan(0); + }); }); }); }); diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts index 55a347600d..21df3ade96 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler.ts @@ -8,9 +8,9 @@ import { BatchedOperation } from './types'; * Generate sample amounts up to `maxFillAmount`. */ export function getSampleAmounts(maxFillAmount: BigNumber, numSamples: number, expBase: number = 1): BigNumber[] { - const distribution = [...Array(numSamples)].map((v, i) => new BigNumber(expBase).pow(i)); + const distribution = [...Array(numSamples)].map((_v, i) => new BigNumber(expBase).pow(i)); const stepSizes = distribution.map(d => d.div(BigNumber.sum(...distribution))); - const amounts = stepSizes.map((s, i) => { + const amounts = stepSizes.map((_s, i) => { return maxFillAmount .times(BigNumber.sum(...[0, ...stepSizes.slice(0, i + 1)])) .integerValue(BigNumber.ROUND_UP); diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts index 9885f8ca0c..c627cd0035 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts @@ -248,10 +248,10 @@ export const samplerOperations = { }, constant(result: T): BatchedOperation { return { - encodeCall: contract => { + encodeCall: _contract => { return '0x'; }, - handleCallResultsAsync: async (contract, callResults) => { + handleCallResultsAsync: async (_contract, _callResults) => { return result; }, }; @@ -406,7 +406,7 @@ export const samplerOperations = { return samples[i].map((output, j) => ({ source: op.source, output, - input: makerFillAmounts[i], + input: makerFillAmounts[j], })); }); }, diff --git a/packages/asset-swapper/src/utils/quote_simulation.ts b/packages/asset-swapper/src/utils/quote_simulation.ts index 0b96f1729e..8310caf8b3 100644 --- a/packages/asset-swapper/src/utils/quote_simulation.ts +++ b/packages/asset-swapper/src/utils/quote_simulation.ts @@ -136,7 +136,7 @@ export function simulateWorstCaseFill(quoteInfo: QuoteFillInfo): QuoteFillResult } export function fillQuoteOrders( - side: MarketOperation, + _side: MarketOperation, fillOrders: QuoteFillOrderCall[], inputAmount: BigNumber, protocolFeePerFillOrder: BigNumber, @@ -335,7 +335,7 @@ function fromIntermediateQuoteFillResult(ir: IntermediateQuoteFillResult, quoteI } export function getFlattenedFillsFromOrders(orders: OptimizedMarketOrder[]): CollapsedFill[] { - const fills = []; + const fills: CollapsedFill[] = []; for (const o of orders) { fills.push(...o.fills); } From 2e80981dff73c09163143e07f332472d3f623149 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Fri, 17 Apr 2020 07:49:51 +1000 Subject: [PATCH 03/11] Deploy mainnet --- .../contracts/src/IERC20BridgeSampler.sol | 15 +++++ .../utils/market_operation_utils/constants.ts | 1 + .../sampler_operations.ts | 18 +++++ packages/contract-addresses/addresses.json | 2 +- .../artifacts/ERC20BridgeSampler.json | 26 +++++++- .../artifacts/IERC20BridgeSampler.json | 22 +++++++ .../erc20_bridge_sampler.ts | 65 +++++++++++++++++++ .../i_erc20_bridge_sampler.ts | 65 +++++++++++++++++++ 8 files changed, 211 insertions(+), 3 deletions(-) diff --git a/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol b/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol index c6650a336a..0f00cd6a95 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol @@ -73,6 +73,21 @@ interface IERC20BridgeSampler { view returns (uint256[] memory makerTokenAmounts); + /// @dev Sample buy quotes from Kyber. + /// @param takerToken Address of the taker token (what to sell). + /// @param makerToken Address of the maker token (what to buy). + /// @param makerTokenAmounts Maker token buy amount for each sample. + /// @return takerTokenAmounts Taker amounts sold at each maker token + /// amount. + function sampleBuysFromKyberNetwork( + address takerToken, + address makerToken, + uint256[] calldata makerTokenAmounts + ) + external + view + returns (uint256[] memory takerTokenAmounts); + /// @dev Sample sell quotes from Eth2Dai/Oasis. /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). diff --git a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts index bfeb729279..f97c01e8b2 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -24,6 +24,7 @@ export const SELL_SOURCES = [ export const BUY_SOURCES = [ ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai, + ERC20BridgeSource.Kyber, // All Curve sources ERC20BridgeSource.CurveUsdcDai, ERC20BridgeSource.CurveUsdcDaiUsdt, diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts index c627cd0035..387f426096 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts @@ -48,6 +48,22 @@ export const samplerOperations = { }, }; }, + getKyberBuyQuotes( + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ): BatchedOperation { + return { + encodeCall: contract => { + return contract + .sampleBuysFromKyberNetwork(takerToken, makerToken, makerFillAmounts) + .getABIEncodedTransactionData(); + }, + handleCallResultsAsync: async (contract, callResults) => { + return contract.getABIDecodedReturnData('sampleBuysFromKyberNetwork', callResults); + }, + }; + }, getUniswapSellQuotes( makerToken: string, takerToken: string, @@ -357,6 +373,8 @@ export const samplerOperations = { batchedOperation = samplerOperations.getEth2DaiBuyQuotes(makerToken, takerToken, makerFillAmounts); } else if (source === ERC20BridgeSource.Uniswap) { batchedOperation = samplerOperations.getUniswapBuyQuotes(makerToken, takerToken, makerFillAmounts); + } else if (source === ERC20BridgeSource.Kyber) { + batchedOperation = samplerOperations.getKyberBuyQuotes(makerToken, takerToken, makerFillAmounts); } else if (Object.keys(DEFAULT_CURVE_OPTS).includes(source)) { const { curveAddress, tokens } = DEFAULT_CURVE_OPTS[source]; const fromTokenIdx = tokens.indexOf(takerToken); diff --git a/packages/contract-addresses/addresses.json b/packages/contract-addresses/addresses.json index 07c7af5e8c..a66d40c8ee 100644 --- a/packages/contract-addresses/addresses.json +++ b/packages/contract-addresses/addresses.json @@ -20,7 +20,7 @@ "devUtils": "0x74134cf88b21383713e096a5ecf59e297dc7f547", "erc20BridgeProxy": "0x8ed95d1746bf1e4dab58d8ed4724f1ef95b20db0", "uniswapBridge": "0x36691c4f426eb8f42f150ebde43069a31cb080ad", - "erc20BridgeSampler": "0xb2dee8cf2a06fbf0942fda5521f890b6e9911bfe", + "erc20BridgeSampler": "0xe603360c47f4cd99026fa01f2da2dec7e23babd9", "kyberBridge": "0x1c29670f7a77f1052d30813a0a4f632c78a02610", "eth2DaiBridge": "0x991c745401d5b5e469b8c3e2cb02c748f08754f1", "chaiBridge": "0x77c31eba23043b9a72d13470f3a3a311344d7438", diff --git a/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json b/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json index d746744107..4161626c96 100644 --- a/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json +++ b/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json @@ -126,6 +126,19 @@ "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [ + { "internalType": "address", "name": "takerToken", "type": "address" }, + { "internalType": "address", "name": "makerToken", "type": "address" }, + { "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" } + ], + "name": "sampleBuysFromKyberNetwork", + "outputs": [{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [ @@ -271,6 +284,15 @@ }, "return": "takerTokenAmounts Taker amounts sold at each maker token amount." }, + "sampleBuysFromKyberNetwork(address,address,uint256[])": { + "details": "Sample buy quotes from Kyber.", + "params": { + "makerToken": "Address of the maker token (what to buy).", + "makerTokenAmounts": "Maker token buy amount for each sample.", + "takerToken": "Address of the taker token (what to sell)." + }, + "return": "takerTokenAmounts Taker amounts sold at each maker token amount." + }, "sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[])": { "details": "Sample buy quotes from an arbitrary on-chain liquidity provider.", "params": { @@ -341,10 +363,10 @@ }, "evm": { "bytecode": { - "object": "0x60806040523480156200001157600080fd5b5060405162002c7838038062002c7883398101604081905262000034916200005a565b600080546001600160a01b0319166001600160a01b03929092169190911790556200008a565b6000602082840312156200006c578081fd5b81516001600160a01b038116811462000083578182fd5b9392505050565b612bde806200009a6000396000f3fe608060405234801561001057600080fd5b50600436106100df5760003560e01c806364ee6ade1161008c5780638b123a02116100665780638b123a02146101b25780639f76ad35146101c5578063a2c28d4b146101d8578063c7f7142e146101eb576100df565b806364ee6ade1461016c57806368be3cf21461017f5780636dd6b78d1461019f576100df565b80634cb8e253116100bd5780634cb8e2531461013357806359f515d01461014657806360ee052a14610159576100df565b80631796fb87146100e4578063354152a31461010d5780634703a7e614610120575b600080fd5b6100f76100f236600461254d565b61020b565b60405161010491906128b4565b60405180910390f35b6100f761011b36600461254d565b6103dc565b6100f761012e3660046124ed565b610596565b6100f76101413660046124ed565b610772565b6100f76101543660046125f7565b610a62565b6100f76101673660046124ed565b610b0b565b6100f761017a3660046124ed565b610dcb565b61019261018d36600461258b565b610f91565b6040516101049190612836565b6100f76101ad3660046124ed565b6110d0565b6100f76101c03660046125f7565b611378565b6100f76101d336600461247a565b611651565b6100f76101e636600461247a565b611856565b6101fe6101f9366004612430565b611a42565b60405161010491906127bd565b60606000825190508060405190808252806020026020018201604052801561023d578160200160208202803883390190505b50915060005b818110156103d257600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff16630e71d1b9905060e01b8a8a8a888151811061029a57fe5b60200260200101516040516024016102b493929190612927565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161033d91906127a1565b6000604051808303818686fa925050503d8060008114610379576040519150601f19603f3d011682016040523d82523d6000602084013e61037e565b606091505b509092509050600082156103a757818060200190516103a09190810190612715565b90506103af565b5050506103d2565b808685815181106103bc57fe5b6020908102919091010152505050600101610243565b5050949350505050565b60606000825190508060405190808252806020026020018201604052801561040e578160200160208202803883390190505b50915060005b818110156103d257600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a888151811061046b57fe5b602002602001015160405160240161048593929190612927565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161050e91906127a1565b6000604051808303818686fa925050503d806000811461054a576040519150601f19603f3d011682016040523d82523d6000602084013e61054f565b606091505b509092509050600082156103a757818060200190516105719190810190612715565b90508086858151811061058057fe5b6020908102919091010152505050600101610414565b60606105a28385611b7d565b81516040805182815260208084028201019091528180156105cd578160200160208202803883390190505b50915060005b8181101561076957600060606105e7611bf0565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a888151811061063157fe5b602002602001015160405160240161064b93929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516106d491906127a1565b6000604051808303818686fa925050503d8060008114610710576040519150601f19603f3d011682016040523d82523d6000602084013e610715565b606091505b5090925090506000821561073e57818060200190516107379190810190612715565b9050610746565b505050610769565b8086858151811061075357fe5b60209081029190910101525050506001016105d3565b50509392505050565b606061077e8385611b7d565b6000610788611c08565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146107c057846107d6565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006107e2611c08565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161461081a5784610830565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b9050600061083d87611c20565b60ff169050600061084d87611c20565b60ff16905060008651905080604051908082528060200260200182016040528015610882578160200160208202803883390190505b50955060005b81811015610a55576000606061089c611c31565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b8a8a8e88815181106108e657fe5b602002602001015160405160240161090093929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161098991906127a1565b6000604051808303818686fa925050503d80600081146109c5576040519150601f19603f3d011682016040523d82523d6000602084013e6109ca565b606091505b509092509050600082156109f357818060200190516109ec9190810190612715565b90506109fb565b505050610a55565b670de0b6b3a764000087600a0a87600a0a8d8781518110610a1857fe5b602002602001015184020281610a2a57fe5b0481610a3257fe5b048a8581518110610a3f57fe5b6020908102919091010152505050600101610888565b5050505050509392505050565b6060610a6e8383611378565b905060005b8351811015610b0457818181518110610a8857fe5b6020026020010151600014610afc57610ae3828281518110610aa657fe5b6020026020010151858381518110610aba57fe5b602002602001015160a00151868481518110610ad257fe5b602002602001015160800151611c49565b828281518110610aef57fe5b6020026020010181815250505b600101610a73565b5092915050565b6060610b178385611b7d565b8151604080518281526020808402820101909152818015610b42578160200160208202803883390190505b5091506000610b4f611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610b8f57610b8a86611c8b565b610b92565b60005b90506000610b9e611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610bde57610bd986611c8b565b610be1565b60005b905060005b83811015610dc0576001610bf8611c08565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415610c8f578651610c6e9085907f2640f62c00000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b6020026020010151611d1d565b878481518110610c7a57fe5b60200260200101819350828152505050610dac565b610c97611c08565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415610d00578651610c6e9084907f59e9486200000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b8651600090610d399085907f59e9486200000000000000000000000000000000000000000000000000000000908b9087908110610c6157fe5b925090508015610d8f57610d6e857f2640f62c0000000000000000000000000000000000000000000000000000000083611d1d565b888581518110610d7a57fe5b60200260200101819450828152505050610daa565b6000878481518110610d9d57fe5b6020026020010181815250505b505b80610db75750610dc0565b50600101610be6565b505050509392505050565b6060610dd78385611b7d565b8151604080518281526020808402820101909152818015610e02578160200160208202803883390190505b50915060005b818110156107695760006060610e1c611bf0565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a8881518110610e6657fe5b6020026020010151604051602401610e8093929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610f0991906127a1565b6000604051808303818686fa925050503d8060008114610f45576040519150601f19603f3d011682016040523d82523d6000602084013e610f4a565b606091505b5090925090506000821561073e5781806020019051610f6c9190810190612715565b905080868581518110610f7b57fe5b6020908102919091010152505050600101610e08565b604080518281526020808402820101909152606090828015610fc757816020015b6060815260200190600190039081610fb25790505b50905060005b808314610b04576000606030868685818110610fe557fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13684900301811261101f57600080fd5b9091016020810191503567ffffffffffffffff81111561103e57600080fd5b3681900382131561104e57600080fd5b60405161105c929190612791565b600060405180830381855afa9150503d8060008114611097576040519150601f19603f3d011682016040523d82523d6000602084013e61109c565b606091505b5091509150816110ae57805160208201fd5b808484815181106110bb57fe5b60209081029190910101525050600101610fcd565b60606110dc8385611b7d565b8151604080518281526020808402820101909152818015611107578160200160208202803883390190505b5091506000611114611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146111545761114f86611c8b565b611157565b60005b90506000611163611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146111a35761119e86611c8b565b6111a6565b60005b905060005b83811015610dc05760016111bd611c08565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614156112475786516112269085907f95b68fe700000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b87848151811061123257fe5b60200260200101819350828152505050611364565b61124f611c08565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156112b85786516112269084907fcd7724c300000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b86516000906112f19086907f95b68fe700000000000000000000000000000000000000000000000000000000908b9087908110610c6157fe5b92509050801561134757611326847fcd7724c30000000000000000000000000000000000000000000000000000000083611d1d565b88858151811061133257fe5b60200260200101819450828152505050611362565b600087848151811061135557fe5b6020026020010181815250505b505b8061136f5750610dc0565b506001016111ab565b606082516040519080825280602002602001820160405280156113a5578160200160208202803883390190505b506000805491925073ffffffffffffffffffffffffffffffffffffffff909116905b84518114611649578381815181106113db57fe5b6020026020010151516000148061140957508481815181106113f957fe5b6020026020010151608001516000145b8061142b575084818151811061141b57fe5b602002602001015160a001516000145b1561144f57600083828151811061143e57fe5b602002602001018181525050611641565b600060608373ffffffffffffffffffffffffffffffffffffffff166207a1208573ffffffffffffffffffffffffffffffffffffffff1663e77286eb905060e01b89868151811061149b57fe5b60200260200101518987815181106114af57fe5b60200260200101516040516024016114c89291906129a2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161155191906127a1565b6000604051808303818686fa925050503d806000811461158d576040519150601f19603f3d011682016040523d82523d6000602084013e611592565b606091505b5091509150816115bd5760008584815181106115aa57fe5b6020026020010181815250505050611641565b6115c561212c565b600080838060200190516115dc91908101906126a8565b919450925090506003835160068111156115f257fe5b1415806115fd575080155b1561162157600088878151811061161057fe5b60200260200101818152505061163b565b8188878151811061162e57fe5b6020026020010181815250505b50505050505b6001016113c7565b505092915050565b606060008251905080604051908082528060200260200182016040528015611683578160200160208202803883390190505b5091506000611693878787611a42565b905073ffffffffffffffffffffffffffffffffffffffff81166116b8575061184e9050565b60005b8281101561184a57600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b888151811061171257fe5b602002602001015160405160240161172c93929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516117b591906127a1565b6000604051808303818686fa925050503d80600081146117f1576040519150601f19603f3d011682016040523d82523d6000602084013e6117f6565b606091505b5090925090506000821561181f57818060200190516118189190810190612715565b9050611827565b50505061184a565b8087858151811061183457fe5b60209081029190910101525050506001016116bb565b5050505b949350505050565b606060008251905080604051908082528060200260200182016040528015611888578160200160208202803883390190505b5091506000611898878787611a42565b905073ffffffffffffffffffffffffffffffffffffffff81166118bd575061184e9050565b60005b8281101561184a57600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff166345060eb0905060e01b8b8b8b888151811061191757fe5b602002602001015160405160240161193193929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516119ba91906127a1565b6000604051808303818686fa925050503d80600081146119f6576040519150601f19603f3d011682016040523d82523d6000602084013e6119fb565b606091505b5090925090506000821561181f5781806020019051611a1d9190810190612715565b905080878581518110611a2c57fe5b60209081029190910101525050506001016118c0565b6040516000906060907f153f59970000000000000000000000000000000000000000000000000000000090611a7d90869086906024016127de565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff1683604051611b0591906127a1565b600060405180830381855afa9150503d8060008114611b40576040519150601f19603f3d011682016040523d82523d6000602084013e611b45565b606091505b5091509150818015611b58575080516020145b15611b7257611b6881600c611e66565b9350505050611b76565b5050505b9392505050565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611bec576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611be390612945565b60405180910390fd5b5050565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b6000611c2b82611eab565b92915050565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b600061184e83611c7f611c6382600163ffffffff611f7c16565b611c73888763ffffffff611f9b16565b9063ffffffff611fcc16565b9063ffffffff611fe816565b6000611c95612012565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b8152600401611ccd91906127bd565b60206040518083038186803b158015611ce557600080fd5b505afa158015611cf9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c2b9190810190612414565b60008073ffffffffffffffffffffffffffffffffffffffff8516611d4057611e5e565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f08686604051602401611d6f9190612af6565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611df891906127a1565b6000604051808303818686fa925050503d8060008114611e34576040519150601f19603f3d011682016040523d82523d6000602084013e611e39565b606091505b5090925090508115611e5c5780806020019051611e599190810190612715565b92505b505b935093915050565b60008160140183511015611e8c57611e8c611e87600485518560140161202a565b6120cf565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce56700000000000000000000000000000000000000000000000000000000815250604051611f0f91906127a1565b600060405180830381855afa9150503d8060008114611f4a576040519150601f19603f3d011682016040523d82523d6000602084013e611f4f565b606091505b5091509150818015611f62575080516020145b15611f7557611f728160006120d7565b92505b5050919050565b600082821115611f9557611f95611e87600285856120e3565b50900390565b600082611faa57506000611c2b565b82820282848281611fb757fe5b0414611b7657611b76611e87600186866120e3565b600082820183811015611b7657611b76611e87600086866120e3565b600081611ffe57611ffe611e87600385856120e3565b600082848161200957fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b6060632800659560e01b84848460405160240161204993929190612919565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b6000611b768383612102565b606063e946c1bb60e01b848484604051602401612049939291906128f7565b6000816020018351101561212357612123611e87600585518560200161202a565b50016020015190565b6040805160608101909152806000815260006020820181905260409091015290565b8035611c2b81612b76565b600082601f830112612169578081fd5b813561217c61217782612b26565b612aff565b8181529150602080830190840160005b838110156121b9576121a4876020843589010161222c565b8352602092830192919091019060010161218c565b5050505092915050565b600082601f8301126121d3578081fd5b81356121e161217782612b26565b81815291506020808301908481018184028601820187101561220257600080fd5b60005b8481101561222157813584529282019290820190600101612205565b505050505092915050565b600082601f83011261223c578081fd5b813567ffffffffffffffff811115612252578182fd5b61228360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601612aff565b915080825283602082850101111561229a57600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b8114611c2b57600080fd5b60006101c08083850312156122d8578182fd5b6122e181612aff565b9150506122ee838361214e565b81526122fd836020840161214e565b602082015261230f836040840161214e565b6040820152612321836060840161214e565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff8082111561238357600080fd5b61238f8683870161222c565b838501526101609250828501359150808211156123ab57600080fd5b6123b78683870161222c565b838501526101809250828501359150808211156123d357600080fd5b6123df8683870161222c565b838501526101a09250828501359150808211156123fb57600080fd5b506124088582860161222c565b82840152505092915050565b600060208284031215612425578081fd5b8151611b7681612b76565b600080600060608486031215612444578182fd5b833561244f81612b76565b9250602084013561245f81612b76565b9150604084013561246f81612b76565b809150509250925092565b6000806000806080858703121561248f578081fd5b843561249a81612b76565b935060208501356124aa81612b76565b925060408501356124ba81612b76565b9150606085013567ffffffffffffffff8111156124d5578182fd5b6124e1878288016121c3565b91505092959194509250565b600080600060608486031215612501578283fd5b833561250c81612b76565b9250602084013561251c81612b76565b9150604084013567ffffffffffffffff811115612537578182fd5b612543868287016121c3565b9150509250925092565b60008060008060808587031215612562578182fd5b843561256d81612b76565b935061257c86602087016122b3565b92506124ba86604087016122b3565b6000806020838503121561259d578182fd5b823567ffffffffffffffff808211156125b4578384fd5b81850186601f8201126125c5578485fd5b80359250818311156125d5578485fd5b86602080850283010111156125e8578485fd5b60200196919550909350505050565b60008060408385031215612609578182fd5b823567ffffffffffffffff80821115612620578384fd5b81850186601f820112612631578485fd5b8035925061264161217784612b26565b83815260208082019190838101885b87811015612679576126678c8484358901016122c5565b85529382019390820190600101612650565b50919750880135945050505080821115612691578283fd5b5061269e85828601612159565b9150509250929050565b600080600083850360a08112156126bd578182fd5b60608112156126ca578182fd5b506126d56060612aff565b8451600781106126e3578283fd5b81526020858101519082015260408086015190820152606085015160808601519194509250801515811461246f578182fd5b600060208284031215612726578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845261275f816020860160208601612b46565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b600082516127b3818460208701612b46565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b6000602080830181845280855180835260408601915060408482028701019250838701855b828110156128a7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452612895858351612747565b9450928501929085019060010161285b565b5092979650505050505050565b602080825282518282018190526000918401906040840190835b818110156128ec5783518352602093840193909201916001016128ce565b509095945050505050565b606081016004851061290557fe5b938152602081019290925260409091015290565b606081016008851061290557fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b6000604082526129b660408301855161272d565b60208401516129c8606084018261272d565b5060408401516129db608084018261272d565b5060608401516129ee60a084018261272d565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c091506101808281870152612a5f610200870185612747565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a0838883030181890152612a9e8287612747565b838b015196508489820301868a0152612ab78188612747565b955050808a0151955050505080858303016101e086015250612ad98183612747565b8481036020860152612aeb8187612747565b979650505050505050565b90815260200190565b60405181810167ffffffffffffffff81118282101715612b1e57600080fd5b604052919050565b600067ffffffffffffffff821115612b3c578081fd5b5060209081020190565b60005b83811015612b61578181015183820152602001612b49565b83811115612b70576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff81168114612b9857600080fd5b5056fea365627a7a72315820fd16ce5ab5cf6ae3213442d55793e044c4eb1e0d181b86e7dc7964a0e1a416f86c6578706572696d656e74616cf564736f6c63430005110040" + "object": "0x60806040523480156200001157600080fd5b50604051620037833803806200378383398101604081905262000034916200005a565b600080546001600160a01b0319166001600160a01b03929092169190911790556200008a565b6000602082840312156200006c578081fd5b81516001600160a01b038116811462000083578182fd5b9392505050565b6136e9806200009a6000396000f3fe608060405234801561001057600080fd5b50600436106100ea5760003560e01c806364ee6ade1161008c5780638b123a02116100665780638b123a02146101d05780639f76ad35146101e3578063a2c28d4b146101f6578063c7f7142e14610209576100ea565b806364ee6ade1461018a57806368be3cf21461019d5780636dd6b78d146101bd576100ea565b80634703a7e6116100c85780634703a7e61461013e5780634cb8e2531461015157806359f515d01461016457806360ee052a14610177576100ea565b80630cc6600b146100ef5780631796fb8714610118578063354152a31461012b575b600080fd5b6101026100fd366004612ff8565b610229565b60405161010f91906133bf565b60405180910390f35b610102610126366004613058565b610944565b610102610139366004613058565b610b15565b61010261014c366004612ff8565b610ccf565b61010261015f366004612ff8565b610eab565b610102610172366004613102565b61119b565b610102610185366004612ff8565b611244565b610102610198366004612ff8565b611504565b6101b06101ab366004613096565b6116ca565b60405161010f9190613341565b6101026101cb366004612ff8565b611809565b6101026101de366004613102565b611ab1565b6101026101f1366004612f85565b611d8a565b610102610204366004612f85565b611f8f565b61021c610217366004612f3b565b612526565b60405161010f91906132c8565b60606102358385612660565b81516102405761093d565b600061024a6126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146102825784610298565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006102a46126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146102dc57846102f2565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006102ff876126eb565b60ff169050600061030f876126eb565b60ff169050855160405190808252806020026020018201604052801561033f578160200160208202803883390190505b50945061034a612c0f565b600060606103566126fc565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b888a8d6000815181106103a157fe5b60200260200101516040516024016103bb93929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161044491906132ac565b6000604051808303818686fa925050503d8060008114610480576040519150601f19603f3d011682016040523d82523d6000602084013e610485565b606091505b509150915081156104ae57808060200190516104a49190810190613220565b60408401526104bb565b5061093d95505050505050565b670de0b6b3a764000084600a0a86600a0a8b6000815181106104d957fe5b602002602001015186604001510202816104ef57fe5b04816104f757fe5b0460208401526105056126fc565b602084015160405173ffffffffffffffffffffffffffffffffffffffff92909216916216e360917f809a9e55000000000000000000000000000000000000000000000000000000009161055f918c918c9190602401613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516105e891906132ac565b6000604051808303818686fa925050503d8060008114610624576040519150601f19603f3d011682016040523d82523d6000602084013e610629565b606091505b50909250905081156104ae57808060200190516106499190810190613220565b6040840152670de0b6b3a764000085600a0a85600a0a8560200151866040015102028161067257fe5b048161067a57fe5b04835260005b895181101561093457600a606085015260005b6106b98b83815181106106a257fe5b602002602001015186600001518760200151612714565b602086018190526106d1906127159061271090612714565b60208601526106de6126fc565b602086015160405173ffffffffffffffffffffffffffffffffffffffff92909216916216e360917f809a9e550000000000000000000000000000000000000000000000000000000091610738918e918e9190602401613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516107c191906132ac565b6000604051808303818686fa925050503d80600081146107fd576040519150601f19603f3d011682016040523d82523d6000602084013e610802565b606091505b509094509250831561082c57828060200190516108229190810190613220565b6040860152610831565b610905565b670de0b6b3a764000087600a0a87600a0a8760200151886040015102028161085557fe5b048161085d57fe5b0485528a518b908390811061086e57fe5b60200260200101518560000151106108c7576127108b838151811061088f57fe5b60200260200101518161089e57fe5b048b83815181106108ab57fe5b6020026020010151866000015103816108c057fe5b0460608601525b8a82815181106108d357fe5b602002602001015185600001511080156108f1575060058560600151115b80156109005750600101600581105b610693575b6109148b83815181106106a257fe5b8a838151811061092057fe5b602090810291909101015250600101610680565b50505050505050505b9392505050565b606060008251905080604051908082528060200260200182016040528015610976578160200160208202803883390190505b50915060005b81811015610b0b57600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff16630e71d1b9905060e01b8a8a8a88815181106109d357fe5b60200260200101516040516024016109ed93929190613432565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610a7691906132ac565b6000604051808303818686fa925050503d8060008114610ab2576040519150601f19603f3d011682016040523d82523d6000602084013e610ab7565b606091505b50909250905060008215610ae05781806020019051610ad99190810190613220565b9050610ae8565b505050610b0b565b80868581518110610af557fe5b602090810291909101015250505060010161097c565b5050949350505050565b606060008251905080604051908082528060200260200182016040528015610b47578160200160208202803883390190505b50915060005b81811015610b0b57600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a8881518110610ba457fe5b6020026020010151604051602401610bbe93929190613432565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610c4791906132ac565b6000604051808303818686fa925050503d8060008114610c83576040519150601f19603f3d011682016040523d82523d6000602084013e610c88565b606091505b50909250905060008215610ae05781806020019051610caa9190810190613220565b905080868581518110610cb957fe5b6020908102919091010152505050600101610b4d565b6060610cdb8385612660565b8151604080518281526020808402820101909152818015610d06578160200160208202803883390190505b50915060005b81811015610ea25760006060610d20612756565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a8881518110610d6a57fe5b6020026020010151604051602401610d8493929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610e0d91906132ac565b6000604051808303818686fa925050503d8060008114610e49576040519150601f19603f3d011682016040523d82523d6000602084013e610e4e565b606091505b50909250905060008215610e775781806020019051610e709190810190613220565b9050610e7f565b505050610ea2565b80868581518110610e8c57fe5b6020908102919091010152505050600101610d0c565b50509392505050565b6060610eb78385612660565b6000610ec16126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614610ef95784610f0f565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b90506000610f1b6126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614610f535784610f69565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b90506000610f76876126eb565b60ff1690506000610f86876126eb565b60ff16905060008651905080604051908082528060200260200182016040528015610fbb578160200160208202803883390190505b50955060005b8181101561118e5760006060610fd56126fc565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b8a8a8e888151811061101f57fe5b602002602001015160405160240161103993929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516110c291906132ac565b6000604051808303818686fa925050503d80600081146110fe576040519150601f19603f3d011682016040523d82523d6000602084013e611103565b606091505b5090925090506000821561112c57818060200190516111259190810190613220565b9050611134565b50505061118e565b670de0b6b3a764000087600a0a87600a0a8d878151811061115157fe5b60200260200101518402028161116357fe5b048161116b57fe5b048a858151811061117857fe5b6020908102919091010152505050600101610fc1565b5050505050509392505050565b60606111a78383611ab1565b905060005b835181101561123d578181815181106111c157fe5b60200260200101516000146112355761121c8282815181106111df57fe5b60200260200101518583815181106111f357fe5b602002602001015160a0015186848151811061120b57fe5b602002602001015160800151612714565b82828151811061122857fe5b6020026020010181815250505b6001016111ac565b5092915050565b60606112508385612660565b815160408051828152602080840282010190915281801561127b578160200160208202803883390190505b50915060006112886126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146112c8576112c38661276e565b6112cb565b60005b905060006112d76126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614611317576113128661276e565b61131a565b60005b905060005b838110156114f95760016113316126d3565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614156113c85786516113a79085907f2640f62c00000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b6020026020010151612800565b8784815181106113b357fe5b602002602001018193508281525050506114e5565b6113d06126d3565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156114395786516113a79084907f59e9486200000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b86516000906114729085907f59e9486200000000000000000000000000000000000000000000000000000000908b908790811061139a57fe5b9250905080156114c8576114a7857f2640f62c0000000000000000000000000000000000000000000000000000000083612800565b8885815181106114b357fe5b602002602001018194508281525050506114e3565b60008784815181106114d657fe5b6020026020010181815250505b505b806114f057506114f9565b5060010161131f565b505050509392505050565b60606115108385612660565b815160408051828152602080840282010190915281801561153b578160200160208202803883390190505b50915060005b81811015610ea25760006060611555612756565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a888151811061159f57fe5b60200260200101516040516024016115b993929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161164291906132ac565b6000604051808303818686fa925050503d806000811461167e576040519150601f19603f3d011682016040523d82523d6000602084013e611683565b606091505b50909250905060008215610e7757818060200190516116a59190810190613220565b9050808685815181106116b457fe5b6020908102919091010152505050600101611541565b60408051828152602080840282010190915260609082801561170057816020015b60608152602001906001900390816116eb5790505b50905060005b80831461123d57600060603086868581811061171e57fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13684900301811261175857600080fd5b9091016020810191503567ffffffffffffffff81111561177757600080fd5b3681900382131561178757600080fd5b60405161179592919061329c565b600060405180830381855afa9150503d80600081146117d0576040519150601f19603f3d011682016040523d82523d6000602084013e6117d5565b606091505b5091509150816117e757805160208201fd5b808484815181106117f457fe5b60209081029190910101525050600101611706565b60606118158385612660565b8151604080518281526020808402820101909152818015611840578160200160208202803883390190505b509150600061184d6126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161461188d576118888661276e565b611890565b60005b9050600061189c6126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146118dc576118d78661276e565b6118df565b60005b905060005b838110156114f95760016118f66126d3565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16141561198057865161195f9085907f95b68fe700000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b87848151811061196b57fe5b60200260200101819350828152505050611a9d565b6119886126d3565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156119f157865161195f9084907fcd7724c300000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b8651600090611a2a9086907f95b68fe700000000000000000000000000000000000000000000000000000000908b908790811061139a57fe5b925090508015611a8057611a5f847fcd7724c30000000000000000000000000000000000000000000000000000000083612800565b888581518110611a6b57fe5b60200260200101819450828152505050611a9b565b6000878481518110611a8e57fe5b6020026020010181815250505b505b80611aa857506114f9565b506001016118e4565b60608251604051908082528060200260200182016040528015611ade578160200160208202803883390190505b506000805491925073ffffffffffffffffffffffffffffffffffffffff909116905b84518114611d8257838181518110611b1457fe5b60200260200101515160001480611b425750848181518110611b3257fe5b6020026020010151608001516000145b80611b645750848181518110611b5457fe5b602002602001015160a001516000145b15611b88576000838281518110611b7757fe5b602002602001018181525050611d7a565b600060608373ffffffffffffffffffffffffffffffffffffffff166207a1208573ffffffffffffffffffffffffffffffffffffffff1663e77286eb905060e01b898681518110611bd457fe5b6020026020010151898781518110611be857fe5b6020026020010151604051602401611c019291906134ad565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611c8a91906132ac565b6000604051808303818686fa925050503d8060008114611cc6576040519150601f19603f3d011682016040523d82523d6000602084013e611ccb565b606091505b509150915081611cf6576000858481518110611ce357fe5b6020026020010181815250505050611d7a565b611cfe612c37565b60008083806020019051611d1591908101906131b3565b91945092509050600383516006811115611d2b57fe5b141580611d36575080155b15611d5a576000888781518110611d4957fe5b602002602001018181525050611d74565b81888781518110611d6757fe5b6020026020010181815250505b50505050505b600101611b00565b505092915050565b606060008251905080604051908082528060200260200182016040528015611dbc578160200160208202803883390190505b5091506000611dcc878787612526565b905073ffffffffffffffffffffffffffffffffffffffff8116611df15750611f879050565b60005b82811015611f8357600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b8881518110611e4b57fe5b6020026020010151604051602401611e6593929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611eee91906132ac565b6000604051808303818686fa925050503d8060008114611f2a576040519150601f19603f3d011682016040523d82523d6000602084013e611f2f565b606091505b50909250905060008215611f585781806020019051611f519190810190613220565b9050611f60565b505050611f83565b80878581518110611f6d57fe5b6020908102919091010152505050600101611df4565b5050505b949350505050565b606060008251905080604051908082528060200260200182016040528015611fc1578160200160208202803883390190505b5091506000611fd1878787612526565b905073ffffffffffffffffffffffffffffffffffffffff81161580611ff4575081155b156120015750611f879050565b612009612c0f565b600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8a8c8b60008151811061205957fe5b602002602001015160405160240161207393929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516120fc91906132ac565b6000604051808303818686fa925050503d8060008114612138576040519150601f19603f3d011682016040523d82523d6000602084013e61213d565b606091505b50915091508115612166578080602001905161215c9190810190613220565b6020840152612171565b50611f879350505050565b602083015160405173ffffffffffffffffffffffffffffffffffffffff86169162030d40917f343fbcdd00000000000000000000000000000000000000000000000000000000916121c8918e918e91602401613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161225191906132ac565b6000604051808303818686fa925050503d806000811461228d576040519150601f19603f3d011682016040523d82523d6000602084013e612292565b606091505b509092509050811561216657808060200190516122b29190810190613220565b835260005b875181101561251857600a606085015260005b6122d98983815181106106a257fe5b602086018190526122f1906127159061271090612714565b8560200181815250508573ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8d8d896020015160405160240161235093929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516123d991906132ac565b6000604051808303818686fa925050503d8060008114612415576040519150601f19603f3d011682016040523d82523d6000602084013e61241a565b606091505b5090945092508315612441578280602001905161243a9190810190613220565b8552612446565b6124e9565b88828151811061245257fe5b60200260200101518560000151106124ab5761271089838151811061247357fe5b60200260200101518161248257fe5b0489838151811061248f57fe5b6020026020010151866000015103816124a457fe5b0460608601525b8882815181106124b757fe5b602002602001015185600001511080156124d5575060058560600151115b80156124e45750600101600581105b6122ca575b6124f88983815181106106a257fe5b88838151811061250457fe5b6020908102919091010152506001016122b7565b505050505050949350505050565b6040516000906060907f153f5997000000000000000000000000000000000000000000000000000000009061256190869086906024016132e9565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff16836040516125e991906132ac565b600060405180830381855afa9150503d8060008114612624576040519150601f19603f3d011682016040523d82523d6000602084013e612629565b606091505b509150915081801561263c575080516020145b156126565761264c81600c612949565b935050505061093d565b5050509392505050565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156126cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016126c690613450565b60405180910390fd5b5050565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b60006126f68261298e565b92915050565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b6000611f878361274a61272e82600163ffffffff612a5f16565b61273e888763ffffffff612a7e16565b9063ffffffff612aaf16565b9063ffffffff612acb16565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b6000612778612af5565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b81526004016127b091906132c8565b60206040518083038186803b1580156127c857600080fd5b505afa1580156127dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126f69190810190612f1f565b60008073ffffffffffffffffffffffffffffffffffffffff851661282357612941565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f086866040516024016128529190613601565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516128db91906132ac565b6000604051808303818686fa925050503d8060008114612917576040519150601f19603f3d011682016040523d82523d6000602084013e61291c565b606091505b509092509050811561293f578080602001905161293c9190810190613220565b92505b505b935093915050565b6000816014018351101561296f5761296f61296a6004855185601401612b0d565b612bb2565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce567000000000000000000000000000000000000000000000000000000008152506040516129f291906132ac565b600060405180830381855afa9150503d8060008114612a2d576040519150601f19603f3d011682016040523d82523d6000602084013e612a32565b606091505b5091509150818015612a45575080516020145b15612a5857612a55816000612bba565b92505b5050919050565b600082821115612a7857612a7861296a60028585612bc6565b50900390565b600082612a8d575060006126f6565b82820282848281612a9a57fe5b041461093d5761093d61296a60018686612bc6565b60008282018381101561093d5761093d61296a60008686612bc6565b600081612ae157612ae161296a60038585612bc6565b6000828481612aec57fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b6060632800659560e01b848484604051602401612b2c93929190613424565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b600061093d8383612be5565b606063e946c1bb60e01b848484604051602401612b2c93929190613402565b60008160200183511015612c0657612c0661296a6005855185602001612b0d565b50016020015190565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b6040805160608101909152806000815260006020820181905260409091015290565b80356126f681613681565b600082601f830112612c74578081fd5b8135612c87612c8282613631565b61360a565b8181529150602080830190840160005b83811015612cc457612caf8760208435890101612d37565b83526020928301929190910190600101612c97565b5050505092915050565b600082601f830112612cde578081fd5b8135612cec612c8282613631565b818152915060208083019084810181840286018201871015612d0d57600080fd5b60005b84811015612d2c57813584529282019290820190600101612d10565b505050505092915050565b600082601f830112612d47578081fd5b813567ffffffffffffffff811115612d5d578182fd5b612d8e60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161360a565b9150808252836020828501011115612da557600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b81146126f657600080fd5b60006101c0808385031215612de3578182fd5b612dec8161360a565b915050612df98383612c59565b8152612e088360208401612c59565b6020820152612e1a8360408401612c59565b6040820152612e2c8360608401612c59565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff80821115612e8e57600080fd5b612e9a86838701612d37565b83850152610160925082850135915080821115612eb657600080fd5b612ec286838701612d37565b83850152610180925082850135915080821115612ede57600080fd5b612eea86838701612d37565b838501526101a0925082850135915080821115612f0657600080fd5b50612f1385828601612d37565b82840152505092915050565b600060208284031215612f30578081fd5b815161093d81613681565b600080600060608486031215612f4f578182fd5b8335612f5a81613681565b92506020840135612f6a81613681565b91506040840135612f7a81613681565b809150509250925092565b60008060008060808587031215612f9a578081fd5b8435612fa581613681565b93506020850135612fb581613681565b92506040850135612fc581613681565b9150606085013567ffffffffffffffff811115612fe0578182fd5b612fec87828801612cce565b91505092959194509250565b60008060006060848603121561300c578283fd5b833561301781613681565b9250602084013561302781613681565b9150604084013567ffffffffffffffff811115613042578182fd5b61304e86828701612cce565b9150509250925092565b6000806000806080858703121561306d578182fd5b843561307881613681565b93506130878660208701612dbe565b9250612fc58660408701612dbe565b600080602083850312156130a8578182fd5b823567ffffffffffffffff808211156130bf578384fd5b81850186601f8201126130d0578485fd5b80359250818311156130e0578485fd5b86602080850283010111156130f3578485fd5b60200196919550909350505050565b60008060408385031215613114578182fd5b823567ffffffffffffffff8082111561312b578384fd5b81850186601f82011261313c578485fd5b8035925061314c612c8284613631565b83815260208082019190838101885b87811015613184576131728c848435890101612dd0565b8552938201939082019060010161315b565b5091975088013594505050508082111561319c578283fd5b506131a985828601612c64565b9150509250929050565b600080600083850360a08112156131c8578182fd5b60608112156131d5578182fd5b506131e0606061360a565b8451600781106131ee578283fd5b815260208581015190820152604080860151908201526060850151608086015191945092508015158114612f7a578182fd5b600060208284031215613231578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845261326a816020860160208601613651565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b600082516132be818460208701613651565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b6000602080830181845280855180835260408601915060408482028701019250838701855b828110156133b2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526133a0858351613252565b94509285019290850190600101613366565b5092979650505050505050565b602080825282518282018190526000918401906040840190835b818110156133f75783518352602093840193909201916001016133d9565b509095945050505050565b606081016004851061341057fe5b938152602081019290925260409091015290565b606081016008851061341057fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b6000604082526134c1604083018551613238565b60208401516134d36060840182613238565b5060408401516134e66080840182613238565b5060608401516134f960a0840182613238565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c09150610180828187015261356a610200870185613252565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a08388830301818901526135a98287613252565b838b015196508489820301868a01526135c28188613252565b955050808a0151955050505080858303016101e0860152506135e48183613252565b84810360208601526135f68187613252565b979650505050505050565b90815260200190565b60405181810167ffffffffffffffff8111828210171561362957600080fd5b604052919050565b600067ffffffffffffffff821115613647578081fd5b5060209081020190565b60005b8381101561366c578181015183820152602001613654565b8381111561367b576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff811681146136a357600080fd5b5056fea365627a7a7231582001492aae0645e3694f9346a9de400473886ee87e4ce809789ba8b68683a5f2876c6578706572696d656e74616cf564736f6c63430005110040" }, "deployedBytecode": { - "object": "0x608060405234801561001057600080fd5b50600436106100df5760003560e01c806364ee6ade1161008c5780638b123a02116100665780638b123a02146101b25780639f76ad35146101c5578063a2c28d4b146101d8578063c7f7142e146101eb576100df565b806364ee6ade1461016c57806368be3cf21461017f5780636dd6b78d1461019f576100df565b80634cb8e253116100bd5780634cb8e2531461013357806359f515d01461014657806360ee052a14610159576100df565b80631796fb87146100e4578063354152a31461010d5780634703a7e614610120575b600080fd5b6100f76100f236600461254d565b61020b565b60405161010491906128b4565b60405180910390f35b6100f761011b36600461254d565b6103dc565b6100f761012e3660046124ed565b610596565b6100f76101413660046124ed565b610772565b6100f76101543660046125f7565b610a62565b6100f76101673660046124ed565b610b0b565b6100f761017a3660046124ed565b610dcb565b61019261018d36600461258b565b610f91565b6040516101049190612836565b6100f76101ad3660046124ed565b6110d0565b6100f76101c03660046125f7565b611378565b6100f76101d336600461247a565b611651565b6100f76101e636600461247a565b611856565b6101fe6101f9366004612430565b611a42565b60405161010491906127bd565b60606000825190508060405190808252806020026020018201604052801561023d578160200160208202803883390190505b50915060005b818110156103d257600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff16630e71d1b9905060e01b8a8a8a888151811061029a57fe5b60200260200101516040516024016102b493929190612927565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161033d91906127a1565b6000604051808303818686fa925050503d8060008114610379576040519150601f19603f3d011682016040523d82523d6000602084013e61037e565b606091505b509092509050600082156103a757818060200190516103a09190810190612715565b90506103af565b5050506103d2565b808685815181106103bc57fe5b6020908102919091010152505050600101610243565b5050949350505050565b60606000825190508060405190808252806020026020018201604052801561040e578160200160208202803883390190505b50915060005b818110156103d257600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a888151811061046b57fe5b602002602001015160405160240161048593929190612927565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161050e91906127a1565b6000604051808303818686fa925050503d806000811461054a576040519150601f19603f3d011682016040523d82523d6000602084013e61054f565b606091505b509092509050600082156103a757818060200190516105719190810190612715565b90508086858151811061058057fe5b6020908102919091010152505050600101610414565b60606105a28385611b7d565b81516040805182815260208084028201019091528180156105cd578160200160208202803883390190505b50915060005b8181101561076957600060606105e7611bf0565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a888151811061063157fe5b602002602001015160405160240161064b93929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516106d491906127a1565b6000604051808303818686fa925050503d8060008114610710576040519150601f19603f3d011682016040523d82523d6000602084013e610715565b606091505b5090925090506000821561073e57818060200190516107379190810190612715565b9050610746565b505050610769565b8086858151811061075357fe5b60209081029190910101525050506001016105d3565b50509392505050565b606061077e8385611b7d565b6000610788611c08565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146107c057846107d6565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006107e2611c08565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161461081a5784610830565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b9050600061083d87611c20565b60ff169050600061084d87611c20565b60ff16905060008651905080604051908082528060200260200182016040528015610882578160200160208202803883390190505b50955060005b81811015610a55576000606061089c611c31565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b8a8a8e88815181106108e657fe5b602002602001015160405160240161090093929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161098991906127a1565b6000604051808303818686fa925050503d80600081146109c5576040519150601f19603f3d011682016040523d82523d6000602084013e6109ca565b606091505b509092509050600082156109f357818060200190516109ec9190810190612715565b90506109fb565b505050610a55565b670de0b6b3a764000087600a0a87600a0a8d8781518110610a1857fe5b602002602001015184020281610a2a57fe5b0481610a3257fe5b048a8581518110610a3f57fe5b6020908102919091010152505050600101610888565b5050505050509392505050565b6060610a6e8383611378565b905060005b8351811015610b0457818181518110610a8857fe5b6020026020010151600014610afc57610ae3828281518110610aa657fe5b6020026020010151858381518110610aba57fe5b602002602001015160a00151868481518110610ad257fe5b602002602001015160800151611c49565b828281518110610aef57fe5b6020026020010181815250505b600101610a73565b5092915050565b6060610b178385611b7d565b8151604080518281526020808402820101909152818015610b42578160200160208202803883390190505b5091506000610b4f611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610b8f57610b8a86611c8b565b610b92565b60005b90506000610b9e611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610bde57610bd986611c8b565b610be1565b60005b905060005b83811015610dc0576001610bf8611c08565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415610c8f578651610c6e9085907f2640f62c00000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b6020026020010151611d1d565b878481518110610c7a57fe5b60200260200101819350828152505050610dac565b610c97611c08565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415610d00578651610c6e9084907f59e9486200000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b8651600090610d399085907f59e9486200000000000000000000000000000000000000000000000000000000908b9087908110610c6157fe5b925090508015610d8f57610d6e857f2640f62c0000000000000000000000000000000000000000000000000000000083611d1d565b888581518110610d7a57fe5b60200260200101819450828152505050610daa565b6000878481518110610d9d57fe5b6020026020010181815250505b505b80610db75750610dc0565b50600101610be6565b505050509392505050565b6060610dd78385611b7d565b8151604080518281526020808402820101909152818015610e02578160200160208202803883390190505b50915060005b818110156107695760006060610e1c611bf0565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a8881518110610e6657fe5b6020026020010151604051602401610e8093929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610f0991906127a1565b6000604051808303818686fa925050503d8060008114610f45576040519150601f19603f3d011682016040523d82523d6000602084013e610f4a565b606091505b5090925090506000821561073e5781806020019051610f6c9190810190612715565b905080868581518110610f7b57fe5b6020908102919091010152505050600101610e08565b604080518281526020808402820101909152606090828015610fc757816020015b6060815260200190600190039081610fb25790505b50905060005b808314610b04576000606030868685818110610fe557fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13684900301811261101f57600080fd5b9091016020810191503567ffffffffffffffff81111561103e57600080fd5b3681900382131561104e57600080fd5b60405161105c929190612791565b600060405180830381855afa9150503d8060008114611097576040519150601f19603f3d011682016040523d82523d6000602084013e61109c565b606091505b5091509150816110ae57805160208201fd5b808484815181106110bb57fe5b60209081029190910101525050600101610fcd565b60606110dc8385611b7d565b8151604080518281526020808402820101909152818015611107578160200160208202803883390190505b5091506000611114611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146111545761114f86611c8b565b611157565b60005b90506000611163611c08565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146111a35761119e86611c8b565b6111a6565b60005b905060005b83811015610dc05760016111bd611c08565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614156112475786516112269085907f95b68fe700000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b87848151811061123257fe5b60200260200101819350828152505050611364565b61124f611c08565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156112b85786516112269084907fcd7724c300000000000000000000000000000000000000000000000000000000908a9086908110610c6157fe5b86516000906112f19086907f95b68fe700000000000000000000000000000000000000000000000000000000908b9087908110610c6157fe5b92509050801561134757611326847fcd7724c30000000000000000000000000000000000000000000000000000000083611d1d565b88858151811061133257fe5b60200260200101819450828152505050611362565b600087848151811061135557fe5b6020026020010181815250505b505b8061136f5750610dc0565b506001016111ab565b606082516040519080825280602002602001820160405280156113a5578160200160208202803883390190505b506000805491925073ffffffffffffffffffffffffffffffffffffffff909116905b84518114611649578381815181106113db57fe5b6020026020010151516000148061140957508481815181106113f957fe5b6020026020010151608001516000145b8061142b575084818151811061141b57fe5b602002602001015160a001516000145b1561144f57600083828151811061143e57fe5b602002602001018181525050611641565b600060608373ffffffffffffffffffffffffffffffffffffffff166207a1208573ffffffffffffffffffffffffffffffffffffffff1663e77286eb905060e01b89868151811061149b57fe5b60200260200101518987815181106114af57fe5b60200260200101516040516024016114c89291906129a2565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161155191906127a1565b6000604051808303818686fa925050503d806000811461158d576040519150601f19603f3d011682016040523d82523d6000602084013e611592565b606091505b5091509150816115bd5760008584815181106115aa57fe5b6020026020010181815250505050611641565b6115c561212c565b600080838060200190516115dc91908101906126a8565b919450925090506003835160068111156115f257fe5b1415806115fd575080155b1561162157600088878151811061161057fe5b60200260200101818152505061163b565b8188878151811061162e57fe5b6020026020010181815250505b50505050505b6001016113c7565b505092915050565b606060008251905080604051908082528060200260200182016040528015611683578160200160208202803883390190505b5091506000611693878787611a42565b905073ffffffffffffffffffffffffffffffffffffffff81166116b8575061184e9050565b60005b8281101561184a57600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b888151811061171257fe5b602002602001015160405160240161172c93929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516117b591906127a1565b6000604051808303818686fa925050503d80600081146117f1576040519150601f19603f3d011682016040523d82523d6000602084013e6117f6565b606091505b5090925090506000821561181f57818060200190516118189190810190612715565b9050611827565b50505061184a565b8087858151811061183457fe5b60209081029190910101525050506001016116bb565b5050505b949350505050565b606060008251905080604051908082528060200260200182016040528015611888578160200160208202803883390190505b5091506000611898878787611a42565b905073ffffffffffffffffffffffffffffffffffffffff81166118bd575061184e9050565b60005b8281101561184a57600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff166345060eb0905060e01b8b8b8b888151811061191757fe5b602002602001015160405160240161193193929190612805565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516119ba91906127a1565b6000604051808303818686fa925050503d80600081146119f6576040519150601f19603f3d011682016040523d82523d6000602084013e6119fb565b606091505b5090925090506000821561181f5781806020019051611a1d9190810190612715565b905080878581518110611a2c57fe5b60209081029190910101525050506001016118c0565b6040516000906060907f153f59970000000000000000000000000000000000000000000000000000000090611a7d90869086906024016127de565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff1683604051611b0591906127a1565b600060405180830381855afa9150503d8060008114611b40576040519150601f19603f3d011682016040523d82523d6000602084013e611b45565b606091505b5091509150818015611b58575080516020145b15611b7257611b6881600c611e66565b9350505050611b76565b5050505b9392505050565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611bec576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611be390612945565b60405180910390fd5b5050565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b6000611c2b82611eab565b92915050565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b600061184e83611c7f611c6382600163ffffffff611f7c16565b611c73888763ffffffff611f9b16565b9063ffffffff611fcc16565b9063ffffffff611fe816565b6000611c95612012565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b8152600401611ccd91906127bd565b60206040518083038186803b158015611ce557600080fd5b505afa158015611cf9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c2b9190810190612414565b60008073ffffffffffffffffffffffffffffffffffffffff8516611d4057611e5e565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f08686604051602401611d6f9190612af6565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611df891906127a1565b6000604051808303818686fa925050503d8060008114611e34576040519150601f19603f3d011682016040523d82523d6000602084013e611e39565b606091505b5090925090508115611e5c5780806020019051611e599190810190612715565b92505b505b935093915050565b60008160140183511015611e8c57611e8c611e87600485518560140161202a565b6120cf565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce56700000000000000000000000000000000000000000000000000000000815250604051611f0f91906127a1565b600060405180830381855afa9150503d8060008114611f4a576040519150601f19603f3d011682016040523d82523d6000602084013e611f4f565b606091505b5091509150818015611f62575080516020145b15611f7557611f728160006120d7565b92505b5050919050565b600082821115611f9557611f95611e87600285856120e3565b50900390565b600082611faa57506000611c2b565b82820282848281611fb757fe5b0414611b7657611b76611e87600186866120e3565b600082820183811015611b7657611b76611e87600086866120e3565b600081611ffe57611ffe611e87600385856120e3565b600082848161200957fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b6060632800659560e01b84848460405160240161204993929190612919565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b6000611b768383612102565b606063e946c1bb60e01b848484604051602401612049939291906128f7565b6000816020018351101561212357612123611e87600585518560200161202a565b50016020015190565b6040805160608101909152806000815260006020820181905260409091015290565b8035611c2b81612b76565b600082601f830112612169578081fd5b813561217c61217782612b26565b612aff565b8181529150602080830190840160005b838110156121b9576121a4876020843589010161222c565b8352602092830192919091019060010161218c565b5050505092915050565b600082601f8301126121d3578081fd5b81356121e161217782612b26565b81815291506020808301908481018184028601820187101561220257600080fd5b60005b8481101561222157813584529282019290820190600101612205565b505050505092915050565b600082601f83011261223c578081fd5b813567ffffffffffffffff811115612252578182fd5b61228360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601612aff565b915080825283602082850101111561229a57600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b8114611c2b57600080fd5b60006101c08083850312156122d8578182fd5b6122e181612aff565b9150506122ee838361214e565b81526122fd836020840161214e565b602082015261230f836040840161214e565b6040820152612321836060840161214e565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff8082111561238357600080fd5b61238f8683870161222c565b838501526101609250828501359150808211156123ab57600080fd5b6123b78683870161222c565b838501526101809250828501359150808211156123d357600080fd5b6123df8683870161222c565b838501526101a09250828501359150808211156123fb57600080fd5b506124088582860161222c565b82840152505092915050565b600060208284031215612425578081fd5b8151611b7681612b76565b600080600060608486031215612444578182fd5b833561244f81612b76565b9250602084013561245f81612b76565b9150604084013561246f81612b76565b809150509250925092565b6000806000806080858703121561248f578081fd5b843561249a81612b76565b935060208501356124aa81612b76565b925060408501356124ba81612b76565b9150606085013567ffffffffffffffff8111156124d5578182fd5b6124e1878288016121c3565b91505092959194509250565b600080600060608486031215612501578283fd5b833561250c81612b76565b9250602084013561251c81612b76565b9150604084013567ffffffffffffffff811115612537578182fd5b612543868287016121c3565b9150509250925092565b60008060008060808587031215612562578182fd5b843561256d81612b76565b935061257c86602087016122b3565b92506124ba86604087016122b3565b6000806020838503121561259d578182fd5b823567ffffffffffffffff808211156125b4578384fd5b81850186601f8201126125c5578485fd5b80359250818311156125d5578485fd5b86602080850283010111156125e8578485fd5b60200196919550909350505050565b60008060408385031215612609578182fd5b823567ffffffffffffffff80821115612620578384fd5b81850186601f820112612631578485fd5b8035925061264161217784612b26565b83815260208082019190838101885b87811015612679576126678c8484358901016122c5565b85529382019390820190600101612650565b50919750880135945050505080821115612691578283fd5b5061269e85828601612159565b9150509250929050565b600080600083850360a08112156126bd578182fd5b60608112156126ca578182fd5b506126d56060612aff565b8451600781106126e3578283fd5b81526020858101519082015260408086015190820152606085015160808601519194509250801515811461246f578182fd5b600060208284031215612726578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845261275f816020860160208601612b46565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b600082516127b3818460208701612b46565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b6000602080830181845280855180835260408601915060408482028701019250838701855b828110156128a7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452612895858351612747565b9450928501929085019060010161285b565b5092979650505050505050565b602080825282518282018190526000918401906040840190835b818110156128ec5783518352602093840193909201916001016128ce565b509095945050505050565b606081016004851061290557fe5b938152602081019290925260409091015290565b606081016008851061290557fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b6000604082526129b660408301855161272d565b60208401516129c8606084018261272d565b5060408401516129db608084018261272d565b5060608401516129ee60a084018261272d565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c091506101808281870152612a5f610200870185612747565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a0838883030181890152612a9e8287612747565b838b015196508489820301868a0152612ab78188612747565b955050808a0151955050505080858303016101e086015250612ad98183612747565b8481036020860152612aeb8187612747565b979650505050505050565b90815260200190565b60405181810167ffffffffffffffff81118282101715612b1e57600080fd5b604052919050565b600067ffffffffffffffff821115612b3c578081fd5b5060209081020190565b60005b83811015612b61578181015183820152602001612b49565b83811115612b70576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff81168114612b9857600080fd5b5056fea365627a7a72315820fd16ce5ab5cf6ae3213442d55793e044c4eb1e0d181b86e7dc7964a0e1a416f86c6578706572696d656e74616cf564736f6c63430005110040" + "object": "0x608060405234801561001057600080fd5b50600436106100ea5760003560e01c806364ee6ade1161008c5780638b123a02116100665780638b123a02146101d05780639f76ad35146101e3578063a2c28d4b146101f6578063c7f7142e14610209576100ea565b806364ee6ade1461018a57806368be3cf21461019d5780636dd6b78d146101bd576100ea565b80634703a7e6116100c85780634703a7e61461013e5780634cb8e2531461015157806359f515d01461016457806360ee052a14610177576100ea565b80630cc6600b146100ef5780631796fb8714610118578063354152a31461012b575b600080fd5b6101026100fd366004612ff8565b610229565b60405161010f91906133bf565b60405180910390f35b610102610126366004613058565b610944565b610102610139366004613058565b610b15565b61010261014c366004612ff8565b610ccf565b61010261015f366004612ff8565b610eab565b610102610172366004613102565b61119b565b610102610185366004612ff8565b611244565b610102610198366004612ff8565b611504565b6101b06101ab366004613096565b6116ca565b60405161010f9190613341565b6101026101cb366004612ff8565b611809565b6101026101de366004613102565b611ab1565b6101026101f1366004612f85565b611d8a565b610102610204366004612f85565b611f8f565b61021c610217366004612f3b565b612526565b60405161010f91906132c8565b60606102358385612660565b81516102405761093d565b600061024a6126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146102825784610298565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006102a46126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146102dc57846102f2565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006102ff876126eb565b60ff169050600061030f876126eb565b60ff169050855160405190808252806020026020018201604052801561033f578160200160208202803883390190505b50945061034a612c0f565b600060606103566126fc565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b888a8d6000815181106103a157fe5b60200260200101516040516024016103bb93929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161044491906132ac565b6000604051808303818686fa925050503d8060008114610480576040519150601f19603f3d011682016040523d82523d6000602084013e610485565b606091505b509150915081156104ae57808060200190516104a49190810190613220565b60408401526104bb565b5061093d95505050505050565b670de0b6b3a764000084600a0a86600a0a8b6000815181106104d957fe5b602002602001015186604001510202816104ef57fe5b04816104f757fe5b0460208401526105056126fc565b602084015160405173ffffffffffffffffffffffffffffffffffffffff92909216916216e360917f809a9e55000000000000000000000000000000000000000000000000000000009161055f918c918c9190602401613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516105e891906132ac565b6000604051808303818686fa925050503d8060008114610624576040519150601f19603f3d011682016040523d82523d6000602084013e610629565b606091505b50909250905081156104ae57808060200190516106499190810190613220565b6040840152670de0b6b3a764000085600a0a85600a0a8560200151866040015102028161067257fe5b048161067a57fe5b04835260005b895181101561093457600a606085015260005b6106b98b83815181106106a257fe5b602002602001015186600001518760200151612714565b602086018190526106d1906127159061271090612714565b60208601526106de6126fc565b602086015160405173ffffffffffffffffffffffffffffffffffffffff92909216916216e360917f809a9e550000000000000000000000000000000000000000000000000000000091610738918e918e9190602401613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516107c191906132ac565b6000604051808303818686fa925050503d80600081146107fd576040519150601f19603f3d011682016040523d82523d6000602084013e610802565b606091505b509094509250831561082c57828060200190516108229190810190613220565b6040860152610831565b610905565b670de0b6b3a764000087600a0a87600a0a8760200151886040015102028161085557fe5b048161085d57fe5b0485528a518b908390811061086e57fe5b60200260200101518560000151106108c7576127108b838151811061088f57fe5b60200260200101518161089e57fe5b048b83815181106108ab57fe5b6020026020010151866000015103816108c057fe5b0460608601525b8a82815181106108d357fe5b602002602001015185600001511080156108f1575060058560600151115b80156109005750600101600581105b610693575b6109148b83815181106106a257fe5b8a838151811061092057fe5b602090810291909101015250600101610680565b50505050505050505b9392505050565b606060008251905080604051908082528060200260200182016040528015610976578160200160208202803883390190505b50915060005b81811015610b0b57600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff16630e71d1b9905060e01b8a8a8a88815181106109d357fe5b60200260200101516040516024016109ed93929190613432565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610a7691906132ac565b6000604051808303818686fa925050503d8060008114610ab2576040519150601f19603f3d011682016040523d82523d6000602084013e610ab7565b606091505b50909250905060008215610ae05781806020019051610ad99190810190613220565b9050610ae8565b505050610b0b565b80868581518110610af557fe5b602090810291909101015250505060010161097c565b5050949350505050565b606060008251905080604051908082528060200260200182016040528015610b47578160200160208202803883390190505b50915060005b81811015610b0b57600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a8881518110610ba457fe5b6020026020010151604051602401610bbe93929190613432565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610c4791906132ac565b6000604051808303818686fa925050503d8060008114610c83576040519150601f19603f3d011682016040523d82523d6000602084013e610c88565b606091505b50909250905060008215610ae05781806020019051610caa9190810190613220565b905080868581518110610cb957fe5b6020908102919091010152505050600101610b4d565b6060610cdb8385612660565b8151604080518281526020808402820101909152818015610d06578160200160208202803883390190505b50915060005b81811015610ea25760006060610d20612756565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a8881518110610d6a57fe5b6020026020010151604051602401610d8493929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610e0d91906132ac565b6000604051808303818686fa925050503d8060008114610e49576040519150601f19603f3d011682016040523d82523d6000602084013e610e4e565b606091505b50909250905060008215610e775781806020019051610e709190810190613220565b9050610e7f565b505050610ea2565b80868581518110610e8c57fe5b6020908102919091010152505050600101610d0c565b50509392505050565b6060610eb78385612660565b6000610ec16126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614610ef95784610f0f565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b90506000610f1b6126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614610f535784610f69565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b90506000610f76876126eb565b60ff1690506000610f86876126eb565b60ff16905060008651905080604051908082528060200260200182016040528015610fbb578160200160208202803883390190505b50955060005b8181101561118e5760006060610fd56126fc565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b8a8a8e888151811061101f57fe5b602002602001015160405160240161103993929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516110c291906132ac565b6000604051808303818686fa925050503d80600081146110fe576040519150601f19603f3d011682016040523d82523d6000602084013e611103565b606091505b5090925090506000821561112c57818060200190516111259190810190613220565b9050611134565b50505061118e565b670de0b6b3a764000087600a0a87600a0a8d878151811061115157fe5b60200260200101518402028161116357fe5b048161116b57fe5b048a858151811061117857fe5b6020908102919091010152505050600101610fc1565b5050505050509392505050565b60606111a78383611ab1565b905060005b835181101561123d578181815181106111c157fe5b60200260200101516000146112355761121c8282815181106111df57fe5b60200260200101518583815181106111f357fe5b602002602001015160a0015186848151811061120b57fe5b602002602001015160800151612714565b82828151811061122857fe5b6020026020010181815250505b6001016111ac565b5092915050565b60606112508385612660565b815160408051828152602080840282010190915281801561127b578160200160208202803883390190505b50915060006112886126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146112c8576112c38661276e565b6112cb565b60005b905060006112d76126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614611317576113128661276e565b61131a565b60005b905060005b838110156114f95760016113316126d3565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614156113c85786516113a79085907f2640f62c00000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b6020026020010151612800565b8784815181106113b357fe5b602002602001018193508281525050506114e5565b6113d06126d3565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156114395786516113a79084907f59e9486200000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b86516000906114729085907f59e9486200000000000000000000000000000000000000000000000000000000908b908790811061139a57fe5b9250905080156114c8576114a7857f2640f62c0000000000000000000000000000000000000000000000000000000083612800565b8885815181106114b357fe5b602002602001018194508281525050506114e3565b60008784815181106114d657fe5b6020026020010181815250505b505b806114f057506114f9565b5060010161131f565b505050509392505050565b60606115108385612660565b815160408051828152602080840282010190915281801561153b578160200160208202803883390190505b50915060005b81811015610ea25760006060611555612756565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a888151811061159f57fe5b60200260200101516040516024016115b993929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161164291906132ac565b6000604051808303818686fa925050503d806000811461167e576040519150601f19603f3d011682016040523d82523d6000602084013e611683565b606091505b50909250905060008215610e7757818060200190516116a59190810190613220565b9050808685815181106116b457fe5b6020908102919091010152505050600101611541565b60408051828152602080840282010190915260609082801561170057816020015b60608152602001906001900390816116eb5790505b50905060005b80831461123d57600060603086868581811061171e57fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13684900301811261175857600080fd5b9091016020810191503567ffffffffffffffff81111561177757600080fd5b3681900382131561178757600080fd5b60405161179592919061329c565b600060405180830381855afa9150503d80600081146117d0576040519150601f19603f3d011682016040523d82523d6000602084013e6117d5565b606091505b5091509150816117e757805160208201fd5b808484815181106117f457fe5b60209081029190910101525050600101611706565b60606118158385612660565b8151604080518281526020808402820101909152818015611840578160200160208202803883390190505b509150600061184d6126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161461188d576118888661276e565b611890565b60005b9050600061189c6126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146118dc576118d78661276e565b6118df565b60005b905060005b838110156114f95760016118f66126d3565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16141561198057865161195f9085907f95b68fe700000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b87848151811061196b57fe5b60200260200101819350828152505050611a9d565b6119886126d3565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156119f157865161195f9084907fcd7724c300000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b8651600090611a2a9086907f95b68fe700000000000000000000000000000000000000000000000000000000908b908790811061139a57fe5b925090508015611a8057611a5f847fcd7724c30000000000000000000000000000000000000000000000000000000083612800565b888581518110611a6b57fe5b60200260200101819450828152505050611a9b565b6000878481518110611a8e57fe5b6020026020010181815250505b505b80611aa857506114f9565b506001016118e4565b60608251604051908082528060200260200182016040528015611ade578160200160208202803883390190505b506000805491925073ffffffffffffffffffffffffffffffffffffffff909116905b84518114611d8257838181518110611b1457fe5b60200260200101515160001480611b425750848181518110611b3257fe5b6020026020010151608001516000145b80611b645750848181518110611b5457fe5b602002602001015160a001516000145b15611b88576000838281518110611b7757fe5b602002602001018181525050611d7a565b600060608373ffffffffffffffffffffffffffffffffffffffff166207a1208573ffffffffffffffffffffffffffffffffffffffff1663e77286eb905060e01b898681518110611bd457fe5b6020026020010151898781518110611be857fe5b6020026020010151604051602401611c019291906134ad565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611c8a91906132ac565b6000604051808303818686fa925050503d8060008114611cc6576040519150601f19603f3d011682016040523d82523d6000602084013e611ccb565b606091505b509150915081611cf6576000858481518110611ce357fe5b6020026020010181815250505050611d7a565b611cfe612c37565b60008083806020019051611d1591908101906131b3565b91945092509050600383516006811115611d2b57fe5b141580611d36575080155b15611d5a576000888781518110611d4957fe5b602002602001018181525050611d74565b81888781518110611d6757fe5b6020026020010181815250505b50505050505b600101611b00565b505092915050565b606060008251905080604051908082528060200260200182016040528015611dbc578160200160208202803883390190505b5091506000611dcc878787612526565b905073ffffffffffffffffffffffffffffffffffffffff8116611df15750611f879050565b60005b82811015611f8357600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b8881518110611e4b57fe5b6020026020010151604051602401611e6593929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611eee91906132ac565b6000604051808303818686fa925050503d8060008114611f2a576040519150601f19603f3d011682016040523d82523d6000602084013e611f2f565b606091505b50909250905060008215611f585781806020019051611f519190810190613220565b9050611f60565b505050611f83565b80878581518110611f6d57fe5b6020908102919091010152505050600101611df4565b5050505b949350505050565b606060008251905080604051908082528060200260200182016040528015611fc1578160200160208202803883390190505b5091506000611fd1878787612526565b905073ffffffffffffffffffffffffffffffffffffffff81161580611ff4575081155b156120015750611f879050565b612009612c0f565b600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8a8c8b60008151811061205957fe5b602002602001015160405160240161207393929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516120fc91906132ac565b6000604051808303818686fa925050503d8060008114612138576040519150601f19603f3d011682016040523d82523d6000602084013e61213d565b606091505b50915091508115612166578080602001905161215c9190810190613220565b6020840152612171565b50611f879350505050565b602083015160405173ffffffffffffffffffffffffffffffffffffffff86169162030d40917f343fbcdd00000000000000000000000000000000000000000000000000000000916121c8918e918e91602401613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161225191906132ac565b6000604051808303818686fa925050503d806000811461228d576040519150601f19603f3d011682016040523d82523d6000602084013e612292565b606091505b509092509050811561216657808060200190516122b29190810190613220565b835260005b875181101561251857600a606085015260005b6122d98983815181106106a257fe5b602086018190526122f1906127159061271090612714565b8560200181815250508573ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8d8d896020015160405160240161235093929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516123d991906132ac565b6000604051808303818686fa925050503d8060008114612415576040519150601f19603f3d011682016040523d82523d6000602084013e61241a565b606091505b5090945092508315612441578280602001905161243a9190810190613220565b8552612446565b6124e9565b88828151811061245257fe5b60200260200101518560000151106124ab5761271089838151811061247357fe5b60200260200101518161248257fe5b0489838151811061248f57fe5b6020026020010151866000015103816124a457fe5b0460608601525b8882815181106124b757fe5b602002602001015185600001511080156124d5575060058560600151115b80156124e45750600101600581105b6122ca575b6124f88983815181106106a257fe5b88838151811061250457fe5b6020908102919091010152506001016122b7565b505050505050949350505050565b6040516000906060907f153f5997000000000000000000000000000000000000000000000000000000009061256190869086906024016132e9565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff16836040516125e991906132ac565b600060405180830381855afa9150503d8060008114612624576040519150601f19603f3d011682016040523d82523d6000602084013e612629565b606091505b509150915081801561263c575080516020145b156126565761264c81600c612949565b935050505061093d565b5050509392505050565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156126cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016126c690613450565b60405180910390fd5b5050565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b60006126f68261298e565b92915050565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b6000611f878361274a61272e82600163ffffffff612a5f16565b61273e888763ffffffff612a7e16565b9063ffffffff612aaf16565b9063ffffffff612acb16565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b6000612778612af5565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b81526004016127b091906132c8565b60206040518083038186803b1580156127c857600080fd5b505afa1580156127dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126f69190810190612f1f565b60008073ffffffffffffffffffffffffffffffffffffffff851661282357612941565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f086866040516024016128529190613601565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516128db91906132ac565b6000604051808303818686fa925050503d8060008114612917576040519150601f19603f3d011682016040523d82523d6000602084013e61291c565b606091505b509092509050811561293f578080602001905161293c9190810190613220565b92505b505b935093915050565b6000816014018351101561296f5761296f61296a6004855185601401612b0d565b612bb2565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce567000000000000000000000000000000000000000000000000000000008152506040516129f291906132ac565b600060405180830381855afa9150503d8060008114612a2d576040519150601f19603f3d011682016040523d82523d6000602084013e612a32565b606091505b5091509150818015612a45575080516020145b15612a5857612a55816000612bba565b92505b5050919050565b600082821115612a7857612a7861296a60028585612bc6565b50900390565b600082612a8d575060006126f6565b82820282848281612a9a57fe5b041461093d5761093d61296a60018686612bc6565b60008282018381101561093d5761093d61296a60008686612bc6565b600081612ae157612ae161296a60038585612bc6565b6000828481612aec57fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b6060632800659560e01b848484604051602401612b2c93929190613424565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b600061093d8383612be5565b606063e946c1bb60e01b848484604051602401612b2c93929190613402565b60008160200183511015612c0657612c0661296a6005855185602001612b0d565b50016020015190565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b6040805160608101909152806000815260006020820181905260409091015290565b80356126f681613681565b600082601f830112612c74578081fd5b8135612c87612c8282613631565b61360a565b8181529150602080830190840160005b83811015612cc457612caf8760208435890101612d37565b83526020928301929190910190600101612c97565b5050505092915050565b600082601f830112612cde578081fd5b8135612cec612c8282613631565b818152915060208083019084810181840286018201871015612d0d57600080fd5b60005b84811015612d2c57813584529282019290820190600101612d10565b505050505092915050565b600082601f830112612d47578081fd5b813567ffffffffffffffff811115612d5d578182fd5b612d8e60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161360a565b9150808252836020828501011115612da557600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b81146126f657600080fd5b60006101c0808385031215612de3578182fd5b612dec8161360a565b915050612df98383612c59565b8152612e088360208401612c59565b6020820152612e1a8360408401612c59565b6040820152612e2c8360608401612c59565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff80821115612e8e57600080fd5b612e9a86838701612d37565b83850152610160925082850135915080821115612eb657600080fd5b612ec286838701612d37565b83850152610180925082850135915080821115612ede57600080fd5b612eea86838701612d37565b838501526101a0925082850135915080821115612f0657600080fd5b50612f1385828601612d37565b82840152505092915050565b600060208284031215612f30578081fd5b815161093d81613681565b600080600060608486031215612f4f578182fd5b8335612f5a81613681565b92506020840135612f6a81613681565b91506040840135612f7a81613681565b809150509250925092565b60008060008060808587031215612f9a578081fd5b8435612fa581613681565b93506020850135612fb581613681565b92506040850135612fc581613681565b9150606085013567ffffffffffffffff811115612fe0578182fd5b612fec87828801612cce565b91505092959194509250565b60008060006060848603121561300c578283fd5b833561301781613681565b9250602084013561302781613681565b9150604084013567ffffffffffffffff811115613042578182fd5b61304e86828701612cce565b9150509250925092565b6000806000806080858703121561306d578182fd5b843561307881613681565b93506130878660208701612dbe565b9250612fc58660408701612dbe565b600080602083850312156130a8578182fd5b823567ffffffffffffffff808211156130bf578384fd5b81850186601f8201126130d0578485fd5b80359250818311156130e0578485fd5b86602080850283010111156130f3578485fd5b60200196919550909350505050565b60008060408385031215613114578182fd5b823567ffffffffffffffff8082111561312b578384fd5b81850186601f82011261313c578485fd5b8035925061314c612c8284613631565b83815260208082019190838101885b87811015613184576131728c848435890101612dd0565b8552938201939082019060010161315b565b5091975088013594505050508082111561319c578283fd5b506131a985828601612c64565b9150509250929050565b600080600083850360a08112156131c8578182fd5b60608112156131d5578182fd5b506131e0606061360a565b8451600781106131ee578283fd5b815260208581015190820152604080860151908201526060850151608086015191945092508015158114612f7a578182fd5b600060208284031215613231578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845261326a816020860160208601613651565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b600082516132be818460208701613651565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b6000602080830181845280855180835260408601915060408482028701019250838701855b828110156133b2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526133a0858351613252565b94509285019290850190600101613366565b5092979650505050505050565b602080825282518282018190526000918401906040840190835b818110156133f75783518352602093840193909201916001016133d9565b509095945050505050565b606081016004851061341057fe5b938152602081019290925260409091015290565b606081016008851061341057fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b6000604082526134c1604083018551613238565b60208401516134d36060840182613238565b5060408401516134e66080840182613238565b5060608401516134f960a0840182613238565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c09150610180828187015261356a610200870185613252565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a08388830301818901526135a98287613252565b838b015196508489820301868a01526135c28188613252565b955050808a0151955050505080858303016101e0860152506135e48183613252565b84810360208601526135f68187613252565b979650505050505050565b90815260200190565b60405181810167ffffffffffffffff8111828210171561362957600080fd5b604052919050565b600067ffffffffffffffff821115613647578081fd5b5060209081020190565b60005b8381101561366c578181015183820152602001613654565b8381111561367b576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff811681146136a357600080fd5b5056fea365627a7a7231582001492aae0645e3694f9346a9de400473886ee87e4ce809789ba8b68683a5f2876c6578706572696d656e74616cf564736f6c63430005110040" } } }, diff --git a/packages/contract-artifacts/artifacts/IERC20BridgeSampler.json b/packages/contract-artifacts/artifacts/IERC20BridgeSampler.json index e82d77ab4a..e62c683ade 100644 --- a/packages/contract-artifacts/artifacts/IERC20BridgeSampler.json +++ b/packages/contract-artifacts/artifacts/IERC20BridgeSampler.json @@ -120,6 +120,19 @@ "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [ + { "internalType": "address", "name": "takerToken", "type": "address" }, + { "internalType": "address", "name": "makerToken", "type": "address" }, + { "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" } + ], + "name": "sampleBuysFromKyberNetwork", + "outputs": [{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [ @@ -265,6 +278,15 @@ }, "return": "takerTokenAmounts Taker amounts sold at each maker token amount." }, + "sampleBuysFromKyberNetwork(address,address,uint256[])": { + "details": "Sample buy quotes from Kyber.", + "params": { + "makerToken": "Address of the maker token (what to buy).", + "makerTokenAmounts": "Maker token buy amount for each sample.", + "takerToken": "Address of the taker token (what to sell)." + }, + "return": "takerTokenAmounts Taker amounts sold at each maker token amount." + }, "sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[])": { "details": "Sample buy quotes from an arbitrary on-chain liquidity provider.", "params": { diff --git a/packages/contract-wrappers/src/generated-wrappers/erc20_bridge_sampler.ts b/packages/contract-wrappers/src/generated-wrappers/erc20_bridge_sampler.ts index efbf89f3c9..458ec915f1 100644 --- a/packages/contract-wrappers/src/generated-wrappers/erc20_bridge_sampler.ts +++ b/packages/contract-wrappers/src/generated-wrappers/erc20_bridge_sampler.ts @@ -448,6 +448,33 @@ export class ERC20BridgeSamplerContract extends BaseContract { stateMutability: 'view', type: 'function', }, + { + constant: true, + inputs: [ + { + name: 'takerToken', + type: 'address', + }, + { + name: 'makerToken', + type: 'address', + }, + { + name: 'makerTokenAmounts', + type: 'uint256[]', + }, + ], + name: 'sampleBuysFromKyberNetwork', + outputs: [ + { + name: 'takerTokenAmounts', + type: 'uint256[]', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, { constant: true, inputs: [ @@ -969,6 +996,44 @@ export class ERC20BridgeSamplerContract extends BaseContract { }, }; } + /** + * Sample buy quotes from Kyber. + * @param takerToken Address of the taker token (what to sell). + * @param makerToken Address of the maker token (what to buy). + * @param makerTokenAmounts Maker token buy amount for each sample. + * @returns takerTokenAmounts Taker amounts sold at each maker token amount. + */ + public sampleBuysFromKyberNetwork( + takerToken: string, + makerToken: string, + makerTokenAmounts: BigNumber[], + ): ContractFunctionObj { + const self = (this as any) as ERC20BridgeSamplerContract; + assert.isString('takerToken', takerToken); + assert.isString('makerToken', makerToken); + assert.isArray('makerTokenAmounts', makerTokenAmounts); + const functionSignature = 'sampleBuysFromKyberNetwork(address,address,uint256[])'; + + return { + async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { + BaseContract._assertCallParams(callData, defaultBlock); + const rawCallResult = await self._performCallAsync( + { ...callData, data: this.getABIEncodedTransactionData() }, + defaultBlock, + ); + const abiEncoder = self._lookupAbiEncoder(functionSignature); + BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder); + return abiEncoder.strictDecodeReturnValue(rawCallResult); + }, + getABIEncodedTransactionData(): string { + return self._strictEncodeArguments(functionSignature, [ + takerToken.toLowerCase(), + makerToken.toLowerCase(), + makerTokenAmounts, + ]); + }, + }; + } /** * Sample buy quotes from an arbitrary on-chain liquidity provider. * @param registryAddress Address of the liquidity provider registry contract. diff --git a/packages/contract-wrappers/src/generated-wrappers/i_erc20_bridge_sampler.ts b/packages/contract-wrappers/src/generated-wrappers/i_erc20_bridge_sampler.ts index 39cb69e78f..c16b69d439 100644 --- a/packages/contract-wrappers/src/generated-wrappers/i_erc20_bridge_sampler.ts +++ b/packages/contract-wrappers/src/generated-wrappers/i_erc20_bridge_sampler.ts @@ -427,6 +427,33 @@ export class IERC20BridgeSamplerContract extends BaseContract { stateMutability: 'view', type: 'function', }, + { + constant: true, + inputs: [ + { + name: 'takerToken', + type: 'address', + }, + { + name: 'makerToken', + type: 'address', + }, + { + name: 'makerTokenAmounts', + type: 'uint256[]', + }, + ], + name: 'sampleBuysFromKyberNetwork', + outputs: [ + { + name: 'takerTokenAmounts', + type: 'uint256[]', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, { constant: true, inputs: [ @@ -946,6 +973,44 @@ export class IERC20BridgeSamplerContract extends BaseContract { }, }; } + /** + * Sample buy quotes from Kyber. + * @param takerToken Address of the taker token (what to sell). + * @param makerToken Address of the maker token (what to buy). + * @param makerTokenAmounts Maker token buy amount for each sample. + * @returns takerTokenAmounts Taker amounts sold at each maker token amount. + */ + public sampleBuysFromKyberNetwork( + takerToken: string, + makerToken: string, + makerTokenAmounts: BigNumber[], + ): ContractFunctionObj { + const self = (this as any) as IERC20BridgeSamplerContract; + assert.isString('takerToken', takerToken); + assert.isString('makerToken', makerToken); + assert.isArray('makerTokenAmounts', makerTokenAmounts); + const functionSignature = 'sampleBuysFromKyberNetwork(address,address,uint256[])'; + + return { + async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { + BaseContract._assertCallParams(callData, defaultBlock); + const rawCallResult = await self._performCallAsync( + { ...callData, data: this.getABIEncodedTransactionData() }, + defaultBlock, + ); + const abiEncoder = self._lookupAbiEncoder(functionSignature); + BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder); + return abiEncoder.strictDecodeReturnValue(rawCallResult); + }, + getABIEncodedTransactionData(): string { + return self._strictEncodeArguments(functionSignature, [ + takerToken.toLowerCase(), + makerToken.toLowerCase(), + makerTokenAmounts, + ]); + }, + }; + } /** * Sample buy quotes from an arbitrary on-chain liquidity provider. * @param registryAddress Address of the liquidity provider registry contract. From 5a3877f27c4832d0caca1c6c48e0d6b859ed3445 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Fri, 17 Apr 2020 12:52:29 +1000 Subject: [PATCH 04/11] Add Kyber rates for buy tests --- packages/asset-swapper/test/market_operation_utils_test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index 1809363c24..e60743d855 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -526,6 +526,7 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.Eth2Dai]: [0.92, 0.1, 0.1, 0.1], // Effectively [0.8, ~0.5, ~0, ~0] [ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2], + [ERC20BridgeSource.Kyber]: [0, 0, 0, 0], }; const feeSchedule = { [ERC20BridgeSource.Uniswap]: FILL_AMOUNT.div(4) @@ -858,6 +859,7 @@ describe('MarketOperationUtils tests', () => { rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.2, 0.1]; rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05]; rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05]; + rates[ERC20BridgeSource.Kyber] = [0, 0, 0, 0]; // Unused replaceSamplerOps({ getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), }); @@ -921,6 +923,7 @@ describe('MarketOperationUtils tests', () => { // Effectively [0.8, ~0.5, ~0, ~0] [ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2], [ERC20BridgeSource.Eth2Dai]: [0.92, 0.1, 0.1, 0.1], + [ERC20BridgeSource.Kyber]: [0, 0, 0, 0], }; const feeSchedule = { [ERC20BridgeSource.Uniswap]: FILL_AMOUNT.div(4) @@ -950,6 +953,7 @@ describe('MarketOperationUtils tests', () => { rates[ERC20BridgeSource.Native] = [0.9, 0.8, 0.5, 0.5]; rates[ERC20BridgeSource.Uniswap] = [0.6, 0.05, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.4, 0.3, 0.01, 0.01]; + rates[ERC20BridgeSource.Kyber] = [0, 0, 0, 0]; replaceSamplerOps({ getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), }); @@ -975,6 +979,7 @@ describe('MarketOperationUtils tests', () => { rates[ERC20BridgeSource.Native] = [1, 1, 0.01, 0.01]; rates[ERC20BridgeSource.Uniswap] = [1, 1, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.49, 0.49, 0.49]; + rates[ERC20BridgeSource.Kyber] = [0, 0, 0, 0]; replaceSamplerOps({ getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), }); @@ -995,6 +1000,7 @@ describe('MarketOperationUtils tests', () => { rates[ERC20BridgeSource.Native] = [0.5, 0.01, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.01, 0.01, 0.01]; rates[ERC20BridgeSource.Uniswap] = [0.48, 0.47, 0.01, 0.01]; + rates[ERC20BridgeSource.Kyber] = [0, 0, 0, 0]; replaceSamplerOps({ getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), }); From e43840c6edb57ce510de8f69073dae2e982b82b5 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Fri, 17 Apr 2020 13:23:25 +1000 Subject: [PATCH 05/11] CHANGELOGs --- contracts/erc20-bridge-sampler/CHANGELOG.json | 7 ++++++- packages/contract-artifacts/CHANGELOG.json | 7 ++++++- packages/contract-wrappers/CHANGELOG.json | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/contracts/erc20-bridge-sampler/CHANGELOG.json b/contracts/erc20-bridge-sampler/CHANGELOG.json index 7b85e2f96c..b42aa9df50 100644 --- a/contracts/erc20-bridge-sampler/CHANGELOG.json +++ b/contracts/erc20-bridge-sampler/CHANGELOG.json @@ -7,7 +7,12 @@ "pr": 2531 }, { - "note": "Sample `Curve` by buy amounts" + "note": "Sample `Curve` for buy amounts", + "pr": 2551 + }, + { + "note": "Added `sampleBuysFromKyberNetwork` ", + "pr": 2551 } ] }, diff --git a/packages/contract-artifacts/CHANGELOG.json b/packages/contract-artifacts/CHANGELOG.json index 615aff17ff..34fa6665c4 100644 --- a/packages/contract-artifacts/CHANGELOG.json +++ b/packages/contract-artifacts/CHANGELOG.json @@ -11,7 +11,12 @@ "pr": 2521 }, { - "note": "Added `ERC20BridgeSampler.sampleBuysFromCurve`" + "note": "Added `ERC20BridgeSampler.sampleBuysFromCurve`", + "pr": 2551 + }, + { + "note": "Added `ERC20BridgeSampler.sampleBuysFromKyberNetwork`", + "pr": 2551 } ] }, diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index 22144e0b0f..8a6bb63677 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -15,7 +15,12 @@ "pr": 2521 }, { - "note": "Added `ERC20BridgeSampler.sampleBuysFromCurve`" + "note": "Added `ERC20BridgeSampler.sampleBuysFromCurve`", + "pr": 2551 + }, + { + "note": "Added `ERC20BridgeSampler.sampleBuysFromKyberNetwork`", + "pr": 2551 } ] }, From 3a8534a567e11c1233a4982c533a78f094a03e17 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Fri, 17 Apr 2020 13:25:46 +1000 Subject: [PATCH 06/11] Provide maxIterations and targetSlippage as options --- .../contracts/src/ERC20BridgeSampler.sol | 48 ++++++++++++------- .../contracts/src/IERC20BridgeSampler.sol | 14 +++++- .../test/erc20-bridge-sampler.ts | 33 +++++++++---- .../bridge_sampler_mainnet_test.ts | 8 +++- 4 files changed, 72 insertions(+), 31 deletions(-) diff --git a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol index 8a3626ca19..b3bccb6001 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol @@ -216,7 +216,6 @@ contract ERC20BridgeSampler is struct FakeBuyContext { uint256 buyAmount; uint256 sellAmount; - uint256 rate; uint256 slippageFromTarget; } @@ -224,12 +223,14 @@ contract ERC20BridgeSampler is /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param makerTokenAmounts Maker token buy amount for each sample. + /// @param opts `FakeBuyOptions` specifying target slippage and max iterations. /// @return takerTokenAmounts Taker amounts sold at each maker token /// amount. function sampleBuysFromKyberNetwork( address takerToken, address makerToken, - uint256[] memory makerTokenAmounts + uint256[] memory makerTokenAmounts, + FakeBuyOptions memory opts ) public view @@ -243,6 +244,7 @@ contract ERC20BridgeSampler is address _makerToken = makerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : makerToken; uint256 takerTokenDecimals = _getTokenDecimals(takerToken); uint256 makerTokenDecimals = _getTokenDecimals(makerToken); + uint256 rate; takerTokenAmounts = new uint256[](makerTokenAmounts.length); FakeBuyContext memory context; // Quote the first amount selling the makerTokenAmount @@ -255,12 +257,12 @@ contract ERC20BridgeSampler is makerTokenAmounts[0] )); if (didSucceed) { - context.rate = abi.decode(resultData, (uint256)); + rate = abi.decode(resultData, (uint256)); } else { return takerTokenAmounts; } // Calculate the initial sell amount from the rate and the maker token sold - context.sellAmount = context.rate * makerTokenAmounts[0] * + context.sellAmount = rate * makerTokenAmounts[0] * 10 ** takerTokenDecimals / 10 ** makerTokenDecimals / 10 ** 18; @@ -274,18 +276,19 @@ contract ERC20BridgeSampler is context.sellAmount )); if (didSucceed) { - context.rate = abi.decode(resultData, (uint256)); + rate = abi.decode(resultData, (uint256)); } else { return takerTokenAmounts; } // Calculate the buy amount from the rate and the amount sold - context.buyAmount = context.rate * context.sellAmount * + context.buyAmount = rate * context.sellAmount * 10 ** makerTokenDecimals / 10 ** takerTokenDecimals / 10 ** 18; for (uint256 i = 0; i < makerTokenAmounts.length; i++) { uint256 iteration = 0; - context.slippageFromTarget = 10; + // Default to some value higher than our target + context.slippageFromTarget = opts.targetSlippageBps + 1; do { // adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER context.sellAmount = LibMath.getPartialAmountCeil( @@ -295,7 +298,7 @@ contract ERC20BridgeSampler is ); // JUMP Multiplier by 1.0005 context.sellAmount = LibMath.getPartialAmountCeil( - 10005, + (10000 + opts.targetSlippageBps), 10000, context.sellAmount ); @@ -309,20 +312,24 @@ contract ERC20BridgeSampler is context.sellAmount )); if (didSucceed) { - context.rate = abi.decode(resultData, (uint256)); + rate = abi.decode(resultData, (uint256)); } else { break; } - context.buyAmount = context.rate * context.sellAmount * + context.buyAmount = rate * context.sellAmount * 10 ** makerTokenDecimals / 10 ** takerTokenDecimals / 10 ** 18; // 0.0005 slippage is the target if (context.buyAmount >= makerTokenAmounts[i]) { - context.slippageFromTarget = (context.buyAmount - makerTokenAmounts[i]) / (makerTokenAmounts[i] / 10000); + context.slippageFromTarget = (context.buyAmount - makerTokenAmounts[i]) / + (makerTokenAmounts[i] / 10000); } - } while ((context.buyAmount < makerTokenAmounts[i] && context.slippageFromTarget > 5) && ++iteration < 5); + } while ( + (context.buyAmount < makerTokenAmounts[i] && context.slippageFromTarget > opts.targetSlippageBps) && + ++iteration < opts.maxIterations + ); // We do our best to close in on the requested amount, but we can either over buy or under buy and exit // if we hit a max iteration limit // We scale the sell amount to get the approximate target @@ -659,13 +666,15 @@ contract ERC20BridgeSampler is /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param makerTokenAmounts Maker token buy amount for each sample. + /// @param opts `FakeBuyOptions` specifying target slippage and max iterations. /// @return takerTokenAmounts Taker amounts sold at each maker token /// amount. function sampleBuysFromLiquidityProviderRegistry( address registryAddress, address takerToken, address makerToken, - uint256[] memory makerTokenAmounts + uint256[] memory makerTokenAmounts, + FakeBuyOptions memory opts ) public view @@ -718,7 +727,8 @@ contract ERC20BridgeSampler is for (uint256 i = 0; i < makerTokenAmounts.length; i++) { uint256 iteration = 0; - context.slippageFromTarget = 10; + // Default to some value higher than our target + context.slippageFromTarget = opts.targetSlippageBps + 1; do { // adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER context.sellAmount = LibMath.getPartialAmountCeil( @@ -728,7 +738,7 @@ contract ERC20BridgeSampler is ); // JUMP Multiplier by 1.0005 context.sellAmount = LibMath.getPartialAmountCeil( - 10005, + (10000 + opts.targetSlippageBps), 10000, context.sellAmount ); @@ -748,9 +758,13 @@ contract ERC20BridgeSampler is } // 0.0005 slippage is the target if (context.buyAmount >= makerTokenAmounts[i]) { - context.slippageFromTarget = (context.buyAmount - makerTokenAmounts[i]) / (makerTokenAmounts[i] / 10000); + context.slippageFromTarget = (context.buyAmount - makerTokenAmounts[i]) / + (makerTokenAmounts[i] / 10000); } - } while ((context.buyAmount < makerTokenAmounts[i] && context.slippageFromTarget > 5) && ++iteration < 5); + } while ( + (context.buyAmount < makerTokenAmounts[i] && context.slippageFromTarget > opts.targetSlippageBps) && + ++iteration < opts.maxIterations + ); // We do our best to close in on the requested amount, but we can either over buy or under buy and exit // if we hit a max iteration limit // We scale the sell amount to get the approximate target diff --git a/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol b/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol index 0f00cd6a95..0084a720f0 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/IERC20BridgeSampler.sol @@ -24,6 +24,11 @@ import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; interface IERC20BridgeSampler { + struct FakeBuyOptions { + uint256 targetSlippageBps; + uint256 maxIterations; + } + /// @dev Call multiple public functions on this contract in a single transaction. /// @param callDatas ABI-encoded call data for each function call. /// @return callResults ABI-encoded results data for each call. @@ -77,12 +82,14 @@ interface IERC20BridgeSampler { /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param makerTokenAmounts Maker token buy amount for each sample. + /// @param opts `FakeBuyOptions` specifying target slippage and max iterations. /// @return takerTokenAmounts Taker amounts sold at each maker token /// amount. function sampleBuysFromKyberNetwork( address takerToken, address makerToken, - uint256[] calldata makerTokenAmounts + uint256[] calldata makerTokenAmounts, + FakeBuyOptions calldata opts ) external view @@ -204,13 +211,16 @@ interface IERC20BridgeSampler { /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param makerTokenAmounts Maker token buy amount for each sample. + /// @param opts `FakeBuyOptions` specifying target slippage and max iterations. /// @return takerTokenAmounts Taker amounts sold at each maker token /// amount. function sampleBuysFromLiquidityProviderRegistry( address registryAddress, address takerToken, address makerToken, - uint256[] calldata makerTokenAmounts + uint256[] calldata makerTokenAmounts, + FakeBuyOptions calldata opts + ) external view diff --git a/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts b/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts index 1e59d3f9a2..8e51823ff6 100644 --- a/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts +++ b/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts @@ -32,6 +32,10 @@ blockchainTests('erc20-bridge-sampler', env => { const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR'; const MAKER_TOKEN = randomAddress(); const TAKER_TOKEN = randomAddress(); + const FAKE_BUY_OPTS = { + targetSlippageBps: new BigNumber(5), + maxIterations: new BigNumber(5), + }; before(async () => { testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync( @@ -402,12 +406,14 @@ blockchainTests('erc20-bridge-sampler', env => { }); it('throws if tokens are the same', async () => { - const tx = testContract.sampleBuysFromKyberNetwork(MAKER_TOKEN, MAKER_TOKEN, []).callAsync(); + const tx = testContract.sampleBuysFromKyberNetwork(MAKER_TOKEN, MAKER_TOKEN, [], FAKE_BUY_OPTS).callAsync(); return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR); }); it('can return no quotes', async () => { - const quotes = await testContract.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, []).callAsync(); + const quotes = await testContract + .sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, [], FAKE_BUY_OPTS) + .callAsync(); expect(quotes).to.deep.eq([]); }); const expectQuotesWithinRange = ( @@ -429,7 +435,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts); const quotes = await testContract - .sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS) .callAsync(); expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); }); @@ -439,7 +445,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts) + .sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -448,7 +454,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts); const quotes = await testContract - .sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts, FAKE_BUY_OPTS) .callAsync(); expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); }); @@ -458,7 +464,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts) + .sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts, FAKE_BUY_OPTS) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -467,7 +473,7 @@ blockchainTests('erc20-bridge-sampler', env => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts); const quotes = await testContract - .sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS) .callAsync(); expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE); }); @@ -477,7 +483,7 @@ blockchainTests('erc20-bridge-sampler', env => { const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT); await enableFailTriggerAsync(); const quotes = await testContract - .sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts) + .sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS) .callAsync(); expect(quotes).to.deep.eq(expectedQuotes); }); @@ -868,7 +874,13 @@ blockchainTests('erc20-bridge-sampler', env => { it('should be able to query buys from the liquidity provider', async () => { const result = await testContract - .sampleBuysFromLiquidityProviderRegistry(registryContract.address, yAsset, xAsset, sampleAmounts) + .sampleBuysFromLiquidityProviderRegistry( + registryContract.address, + yAsset, + xAsset, + sampleAmounts, + FAKE_BUY_OPTS, + ) .callAsync(); result.forEach((value, idx) => { expect(value).is.bignumber.eql(sampleAmounts[idx].plus(1)); @@ -882,6 +894,7 @@ blockchainTests('erc20-bridge-sampler', env => { yAsset, randomAddress(), sampleAmounts, + FAKE_BUY_OPTS, ) .callAsync(); result.forEach(value => { @@ -891,7 +904,7 @@ blockchainTests('erc20-bridge-sampler', env => { it('should just return zeros if the registry does not exist', async () => { const result = await testContract - .sampleBuysFromLiquidityProviderRegistry(randomAddress(), yAsset, xAsset, sampleAmounts) + .sampleBuysFromLiquidityProviderRegistry(randomAddress(), yAsset, xAsset, sampleAmounts, FAKE_BUY_OPTS) .callAsync(); result.forEach(value => { expect(value).is.bignumber.eql(constants.ZERO_AMOUNT); diff --git a/contracts/integrations/test/bridge_sampler/bridge_sampler_mainnet_test.ts b/contracts/integrations/test/bridge_sampler/bridge_sampler_mainnet_test.ts index 83d3ef2e3b..78cdc8dac1 100644 --- a/contracts/integrations/test/bridge_sampler/bridge_sampler_mainnet_test.ts +++ b/contracts/integrations/test/bridge_sampler/bridge_sampler_mainnet_test.ts @@ -66,6 +66,10 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => { }); }); describe('Kyber', () => { + const FAKE_BUY_OPTS = { + targetSlippageBps: new BigNumber(5), + maxIterations: new BigNumber(5), + }; const DAI_ADDRESS = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; const WETH_ADDRESS = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; @@ -74,7 +78,7 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => { // From ETH to DAI // I want to buy 1 DAI const samples = await testContract - .sampleBuysFromKyberNetwork(WETH_ADDRESS, DAI_ADDRESS, [toBaseUnitAmount(1)]) + .sampleBuysFromKyberNetwork(WETH_ADDRESS, DAI_ADDRESS, [toBaseUnitAmount(1)], FAKE_BUY_OPTS) .callAsync(); expect(samples.length).to.be.bignumber.greaterThan(0); expect(samples[0]).to.be.bignumber.greaterThan(0); @@ -84,7 +88,7 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => { // From USDC to DAI // I want to buy 1 WETH const samples = await testContract - .sampleBuysFromKyberNetwork(DAI_ADDRESS, WETH_ADDRESS, [toBaseUnitAmount(1)]) + .sampleBuysFromKyberNetwork(DAI_ADDRESS, WETH_ADDRESS, [toBaseUnitAmount(1)], FAKE_BUY_OPTS) .callAsync(); expect(samples.length).to.be.bignumber.greaterThan(0); expect(samples[0]).to.be.bignumber.greaterThan(0); From bf7f8b61f02935bc76fb661306adb731de7560fe Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Fri, 17 Apr 2020 15:56:09 +1000 Subject: [PATCH 07/11] Cleanup ERC20BridgeSampler for re-use --- .../contracts/src/ERC20BridgeSampler.sol | 344 ++++++++---------- .../test/erc20-bridge-sampler.ts | 16 +- 2 files changed, 157 insertions(+), 203 deletions(-) diff --git a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol index b3bccb6001..a30e045e15 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol @@ -213,12 +213,6 @@ contract ERC20BridgeSampler is } } - struct FakeBuyContext { - uint256 buyAmount; - uint256 sellAmount; - uint256 slippageFromTarget; - } - /// @dev Sample buy quotes from Kyber. /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). @@ -236,109 +230,14 @@ contract ERC20BridgeSampler is view returns (uint256[] memory takerTokenAmounts) { - _assertValidPair(makerToken, takerToken); - if (makerTokenAmounts.length == 0) { - return takerTokenAmounts; - } - address _takerToken = takerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : takerToken; - address _makerToken = makerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : makerToken; - uint256 takerTokenDecimals = _getTokenDecimals(takerToken); - uint256 makerTokenDecimals = _getTokenDecimals(makerToken); - uint256 rate; - takerTokenAmounts = new uint256[](makerTokenAmounts.length); - FakeBuyContext memory context; - // Quote the first amount selling the makerTokenAmount - (bool didSucceed, bytes memory resultData) = - _getKyberNetworkProxyAddress().staticcall.gas(KYBER_CALL_GAS)( - abi.encodeWithSelector( - IKyberNetwork(0).getExpectedRate.selector, - _makerToken, - _takerToken, - makerTokenAmounts[0] - )); - if (didSucceed) { - rate = abi.decode(resultData, (uint256)); - } else { - return takerTokenAmounts; - } - // Calculate the initial sell amount from the rate and the maker token sold - context.sellAmount = rate * makerTokenAmounts[0] * - 10 ** takerTokenDecimals / - 10 ** makerTokenDecimals / - 10 ** 18; - - (didSucceed, resultData) = - _getKyberNetworkProxyAddress().staticcall.gas(KYBER_CALL_GAS)( - abi.encodeWithSelector( - IKyberNetwork(0).getExpectedRate.selector, - _takerToken, - _makerToken, - context.sellAmount - )); - if (didSucceed) { - rate = abi.decode(resultData, (uint256)); - } else { - return takerTokenAmounts; - } - // Calculate the buy amount from the rate and the amount sold - context.buyAmount = rate * context.sellAmount * - 10 ** makerTokenDecimals / - 10 ** takerTokenDecimals / - 10 ** 18; - for (uint256 i = 0; i < makerTokenAmounts.length; i++) { - uint256 iteration = 0; - // Default to some value higher than our target - context.slippageFromTarget = opts.targetSlippageBps + 1; - do { - // adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER - context.sellAmount = LibMath.getPartialAmountCeil( - makerTokenAmounts[i], - context.buyAmount, - context.sellAmount - ); - // JUMP Multiplier by 1.0005 - context.sellAmount = LibMath.getPartialAmountCeil( - (10000 + opts.targetSlippageBps), - 10000, - context.sellAmount - ); - - (didSucceed, resultData) = - _getKyberNetworkProxyAddress().staticcall.gas(KYBER_CALL_GAS)( - abi.encodeWithSelector( - IKyberNetwork(0).getExpectedRate.selector, - _takerToken, - _makerToken, - context.sellAmount - )); - if (didSucceed) { - rate = abi.decode(resultData, (uint256)); - } else { - break; - } - - context.buyAmount = rate * context.sellAmount * - 10 ** makerTokenDecimals / - 10 ** takerTokenDecimals / - 10 ** 18; - // 0.0005 slippage is the target - if (context.buyAmount >= makerTokenAmounts[i]) { - context.slippageFromTarget = (context.buyAmount - makerTokenAmounts[i]) / - (makerTokenAmounts[i] / 10000); - } - } while ( - (context.buyAmount < makerTokenAmounts[i] && context.slippageFromTarget > opts.targetSlippageBps) && - ++iteration < opts.maxIterations - ); - // We do our best to close in on the requested amount, but we can either over buy or under buy and exit - // if we hit a max iteration limit - // We scale the sell amount to get the approximate target - takerTokenAmounts[i] = LibMath.getPartialAmountCeil( - makerTokenAmounts[i], - context.buyAmount, - context.sellAmount - ); - } + return _sampleApproximateBuysFromSource( + takerToken, + makerToken, + makerTokenAmounts, + opts, + this.sampleSellsFromKyberNetwork.selector, + address(0) // PLP registry address + ); } /// @dev Sample sell quotes from Eth2Dai/Oasis. @@ -680,100 +579,14 @@ contract ERC20BridgeSampler is view returns (uint256[] memory takerTokenAmounts) { - // Initialize array of taker token amounts. - uint256 numSamples = makerTokenAmounts.length; - takerTokenAmounts = new uint256[](numSamples); - - // Query registry for provider address. - address providerAddress = getLiquidityProviderFromRegistry( - registryAddress, + return _sampleApproximateBuysFromSource( takerToken, - makerToken + makerToken, + makerTokenAmounts, + opts, + this.sampleSellsFromLiquidityProviderRegistry.selector, + registryAddress ); - // If provider doesn't exist, return all zeros. - if (providerAddress == address(0) || numSamples == 0) { - return takerTokenAmounts; - } - - FakeBuyContext memory context; - // Quote the first amount selling the makerTokenAmount - (bool didSucceed, bytes memory resultData) = - providerAddress.staticcall.gas(DEFAULT_CALL_GAS)( - abi.encodeWithSelector( - ILiquidityProvider(0).getSellQuote.selector, - makerToken, - takerToken, - makerTokenAmounts[0] - )); - if (didSucceed) { - context.sellAmount = abi.decode(resultData, (uint256)); - } else { - return takerTokenAmounts; - } - - (didSucceed, resultData) = - providerAddress.staticcall.gas(DEFAULT_CALL_GAS)( - abi.encodeWithSelector( - ILiquidityProvider(0).getSellQuote.selector, - takerToken, - makerToken, - context.sellAmount - )); - if (didSucceed) { - context.buyAmount = abi.decode(resultData, (uint256)); - } else { - return takerTokenAmounts; - } - - for (uint256 i = 0; i < makerTokenAmounts.length; i++) { - uint256 iteration = 0; - // Default to some value higher than our target - context.slippageFromTarget = opts.targetSlippageBps + 1; - do { - // adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER - context.sellAmount = LibMath.getPartialAmountCeil( - makerTokenAmounts[i], - context.buyAmount, - context.sellAmount - ); - // JUMP Multiplier by 1.0005 - context.sellAmount = LibMath.getPartialAmountCeil( - (10000 + opts.targetSlippageBps), - 10000, - context.sellAmount - ); - - (didSucceed, resultData) = - providerAddress.staticcall.gas(DEFAULT_CALL_GAS)( - abi.encodeWithSelector( - ILiquidityProvider(0).getSellQuote.selector, - takerToken, - makerToken, - context.sellAmount - )); - if (didSucceed) { - context.buyAmount = abi.decode(resultData, (uint256)); - } else { - break; - } - // 0.0005 slippage is the target - if (context.buyAmount >= makerTokenAmounts[i]) { - context.slippageFromTarget = (context.buyAmount - makerTokenAmounts[i]) / - (makerTokenAmounts[i] / 10000); - } - } while ( - (context.buyAmount < makerTokenAmounts[i] && context.slippageFromTarget > opts.targetSlippageBps) && - ++iteration < opts.maxIterations - ); - // We do our best to close in on the requested amount, but we can either over buy or under buy and exit - // if we hit a max iteration limit - // We scale the sell amount to get the approximate target - takerTokenAmounts[i] = LibMath.getPartialAmountCeil( - makerTokenAmounts[i], - context.buyAmount, - context.sellAmount - ); - } } /// @dev Returns the address of a liquidity provider for the given market @@ -867,4 +680,133 @@ contract ERC20BridgeSampler is { require(makerToken != takerToken, "ERC20BridgeSampler/INVALID_TOKEN_PAIR"); } + + function _sampleSellForApproximateBuy( + address takerToken, + address makerToken, + uint256 makerTokenAmount, + bytes4 selector, + address plpRegistryAddress + ) + private + view + returns (uint256 takerTokenAmount) + { + bytes memory callData; + uint256[] memory tmpTakerAmounts = new uint256[](1); + tmpTakerAmounts[0] = makerTokenAmount; + if (selector == this.sampleSellsFromKyberNetwork.selector) { + callData = abi.encodeWithSelector( + this.sampleSellsFromKyberNetwork.selector, + takerToken, + makerToken, + tmpTakerAmounts + ); + } else { + callData = abi.encodeWithSelector( + this.sampleSellsFromLiquidityProviderRegistry.selector, + plpRegistryAddress, + takerToken, + makerToken, + tmpTakerAmounts + ); + } + (bool success, bytes memory resultData) = address(this).staticcall(callData); + if (!success) { + return 0; + } + // solhint-disable indent + takerTokenAmount = abi.decode(resultData, (uint256[]))[0]; + } + + function _sampleApproximateBuysFromSource( + address takerToken, + address makerToken, + uint256[] memory makerTokenAmounts, + FakeBuyOptions memory opts, + bytes4 selector, + address plpRegistryAddress + ) + private + view + returns (uint256[] memory takerTokenAmounts) + { + _assertValidPair(makerToken, takerToken); + if (makerTokenAmounts.length == 0) { + return takerTokenAmounts; + } + uint256 sellAmount; + uint256 buyAmount; + uint256 slippageFromTarget; + takerTokenAmounts = new uint256[](makerTokenAmounts.length); + sellAmount = _sampleSellForApproximateBuy( + makerToken, + takerToken, + makerTokenAmounts[0], + selector, + plpRegistryAddress + ); + + if (sellAmount == 0) { + return takerTokenAmounts; + } + + buyAmount = _sampleSellForApproximateBuy( + takerToken, + makerToken, + sellAmount, + selector, + plpRegistryAddress + ); + if (buyAmount == 0) { + return takerTokenAmounts; + } + + for (uint256 i = 0; i < makerTokenAmounts.length; i++) { + uint256 iteration = 0; + // Default to some value higher than our target + slippageFromTarget = opts.targetSlippageBps + 1; + do { + // adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER + sellAmount = LibMath.getPartialAmountCeil( + makerTokenAmounts[i], + buyAmount, + sellAmount + ); + // JUMP Multiplier by 1.0005 + sellAmount = LibMath.getPartialAmountCeil( + (10000 + opts.targetSlippageBps), + 10000, + sellAmount + ); + buyAmount = _sampleSellForApproximateBuy( + takerToken, + makerToken, + sellAmount, + selector, + plpRegistryAddress + ); + if (buyAmount == 0) { + break; + } + + // 0.0005 slippage is the target + if (buyAmount >= makerTokenAmounts[i]) { + slippageFromTarget = (buyAmount - makerTokenAmounts[i]) / + (makerTokenAmounts[i] / 10000); + } + } while ( + (buyAmount < makerTokenAmounts[i] && slippageFromTarget > opts.targetSlippageBps) && + ++iteration < opts.maxIterations + ); + // We do our best to close in on the requested amount, but we can either over buy or under buy and exit + // if we hit a max iteration limit + // We scale the sell amount to get the approximate target + takerTokenAmounts[i] = LibMath.getPartialAmountCeil( + makerTokenAmounts[i], + buyAmount, + sellAmount + ); + } + } } diff --git a/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts b/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts index 8e51823ff6..ecb243179b 100644 --- a/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts +++ b/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts @@ -422,12 +422,24 @@ blockchainTests('erc20-bridge-sampler', env => { maxSlippage: BigNumber | number, ) => { quotes.map((_q, i) => { + // If we're within 1 base unit of a low decimal token + // then that's as good as we're going to get (and slippage is "high") + if ( + expectedQuotes[i].isZero() || + BigNumber.max(expectedQuotes[i], quotes[i]) + .minus(BigNumber.min(expectedQuotes[i], quotes[i])) + .eq(1) + ) { + return; + } const slippage = quotes[i] .dividedBy(expectedQuotes[i]) .minus(1) .decimalPlaces(4); - expect(slippage, `quote[${i}]: ${slippage}`).to.be.bignumber.gte(0); - expect(slippage, `quote[${i}] ${slippage}`).to.be.bignumber.lte(new BigNumber(maxSlippage)); + expect(slippage, `quote[${i}]: ${slippage} ${quotes[i]} ${expectedQuotes[i]}`).to.be.bignumber.gte(0); + expect(slippage, `quote[${i}] ${slippage} ${quotes[i]} ${expectedQuotes[i]}`).to.be.bignumber.lte( + new BigNumber(maxSlippage), + ); }); }; From f08a76facb6f7efc3d14765772627d67bb54a1a7 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Sat, 18 Apr 2020 12:16:56 +1000 Subject: [PATCH 08/11] Redeploy Mainnet Kovan --- .../utils/market_operation_utils/constants.ts | 7 +++- .../sampler_operations.ts | 20 ++++++--- .../src/utils/market_operation_utils/types.ts | 9 ++++ packages/contract-addresses/addresses.json | 4 +- .../artifacts/ERC20BridgeSampler.json | 32 ++++++++++++--- .../artifacts/IERC20BridgeSampler.json | 28 +++++++++++-- .../erc20_bridge_sampler.ts | 41 ++++++++++++++++++- .../i_erc20_bridge_sampler.ts | 41 ++++++++++++++++++- 8 files changed, 160 insertions(+), 22 deletions(-) diff --git a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts index f97c01e8b2..e12c51c1f9 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/constants.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/constants.ts @@ -1,6 +1,6 @@ import { BigNumber } from '@0x/utils'; -import { ERC20BridgeSource, GetMarketOrdersOpts } from './types'; +import { ERC20BridgeSource, FakeBuyOpts, GetMarketOrdersOpts } from './types'; // tslint:disable: custom-no-magic-numbers @@ -46,6 +46,11 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = { shouldBatchBridgeOrders: true, }; +export const DEFAULT_FAKE_BUY_OPTS: FakeBuyOpts = { + targetSlippageBps: new BigNumber(5), + maxIterations: new BigNumber(5), +}; + /** * Sources to poll for ETH fee price estimates. */ diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts index 387f426096..17df10c200 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts @@ -1,7 +1,7 @@ import { BigNumber, ERC20BridgeSource, SignedOrder } from '../..'; -import { DEFAULT_CURVE_OPTS } from './constants'; -import { BatchedOperation, DexSample } from './types'; +import { DEFAULT_CURVE_OPTS, DEFAULT_FAKE_BUY_OPTS } from './constants'; +import { BatchedOperation, DexSample, FakeBuyOpts } from './types'; /** * Composable operations that can be batched in a single transaction, @@ -52,11 +52,12 @@ export const samplerOperations = { makerToken: string, takerToken: string, makerFillAmounts: BigNumber[], + fakeBuyOpts: FakeBuyOpts, ): BatchedOperation { return { encodeCall: contract => { return contract - .sampleBuysFromKyberNetwork(takerToken, makerToken, makerFillAmounts) + .sampleBuysFromKyberNetwork(takerToken, makerToken, makerFillAmounts, fakeBuyOpts) .getABIEncodedTransactionData(); }, handleCallResultsAsync: async (contract, callResults) => { @@ -110,6 +111,7 @@ export const samplerOperations = { makerToken: string, takerToken: string, makerFillAmounts: BigNumber[], + fakeBuyOpts: FakeBuyOpts, ): BatchedOperation { return { encodeCall: contract => { @@ -119,6 +121,7 @@ export const samplerOperations = { takerToken, makerToken, makerFillAmounts, + fakeBuyOpts, ) .getABIEncodedTransactionData(); }, @@ -365,6 +368,7 @@ export const samplerOperations = { takerToken: string, makerFillAmounts: BigNumber[], liquidityProviderRegistryAddress?: string | undefined, + fakeBuyOpts: FakeBuyOpts = DEFAULT_FAKE_BUY_OPTS, ): BatchedOperation { const subOps = sources .map(source => { @@ -374,13 +378,18 @@ export const samplerOperations = { } else if (source === ERC20BridgeSource.Uniswap) { batchedOperation = samplerOperations.getUniswapBuyQuotes(makerToken, takerToken, makerFillAmounts); } else if (source === ERC20BridgeSource.Kyber) { - batchedOperation = samplerOperations.getKyberBuyQuotes(makerToken, takerToken, makerFillAmounts); + batchedOperation = samplerOperations.getKyberBuyQuotes( + makerToken, + takerToken, + makerFillAmounts, + fakeBuyOpts, + ); } else if (Object.keys(DEFAULT_CURVE_OPTS).includes(source)) { const { curveAddress, tokens } = DEFAULT_CURVE_OPTS[source]; const fromTokenIdx = tokens.indexOf(takerToken); const toTokenIdx = tokens.indexOf(makerToken); if (fromTokenIdx !== -1 && toTokenIdx !== -1) { - batchedOperation = samplerOperations.getCurveSellQuotes( + batchedOperation = samplerOperations.getCurveBuyQuotes( curveAddress, fromTokenIdx, toTokenIdx, @@ -398,6 +407,7 @@ export const samplerOperations = { makerToken, takerToken, makerFillAmounts, + fakeBuyOpts, ); } else { throw new Error(`Unsupported buy sample source: ${source}`); diff --git a/packages/asset-swapper/src/utils/market_operation_utils/types.ts b/packages/asset-swapper/src/utils/market_operation_utils/types.ts index b2d372a6e3..acff69ab42 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/types.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/types.ts @@ -197,3 +197,12 @@ export interface BatchedOperation { encodeCall(contract: IERC20BridgeSamplerContract): string; handleCallResultsAsync(contract: IERC20BridgeSamplerContract, callResults: string): Promise; } + +/** + * Used in the ERC20BridgeSampler when a source does not natively + * support sampling via a specific buy amount. + */ +export interface FakeBuyOpts { + targetSlippageBps: BigNumber; + maxIterations: BigNumber; +} diff --git a/packages/contract-addresses/addresses.json b/packages/contract-addresses/addresses.json index a66d40c8ee..f159a55077 100644 --- a/packages/contract-addresses/addresses.json +++ b/packages/contract-addresses/addresses.json @@ -20,7 +20,7 @@ "devUtils": "0x74134cf88b21383713e096a5ecf59e297dc7f547", "erc20BridgeProxy": "0x8ed95d1746bf1e4dab58d8ed4724f1ef95b20db0", "uniswapBridge": "0x36691c4f426eb8f42f150ebde43069a31cb080ad", - "erc20BridgeSampler": "0xe603360c47f4cd99026fa01f2da2dec7e23babd9", + "erc20BridgeSampler": "0x867d1b78e7b5c2ac060e7bdeec6d8419a5c6ca4b", "kyberBridge": "0x1c29670f7a77f1052d30813a0a4f632c78a02610", "eth2DaiBridge": "0x991c745401d5b5e469b8c3e2cb02c748f08754f1", "chaiBridge": "0x77c31eba23043b9a72d13470f3a3a311344d7438", @@ -120,7 +120,7 @@ "erc20BridgeProxy": "0xfb2dd2a1366de37f7241c83d47da58fd503e2c64", "uniswapBridge": "0x0e85f89f29998df65402391478e5924700c0079d", "eth2DaiBridge": "0x2d47147429b474d2e4f83e658015858a1312ed5b", - "erc20BridgeSampler": "0xd3fccc4af0732e99290a57bbc2cc2afcbd08283f", + "erc20BridgeSampler": "0x1fdfcf612026c8deed586353430821bee6330dc8", "kyberBridge": "0xaecfa25920f892b6eb496e1f6e84037f59da7f44", "chaiBridge": "0x0000000000000000000000000000000000000000", "dydxBridge": "0x3be8e59038d8c4e8d8776ca40ef2f024bad95ad1", diff --git a/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json b/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json index 4161626c96..c33726025c 100644 --- a/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json +++ b/packages/contract-artifacts/artifacts/ERC20BridgeSampler.json @@ -131,7 +131,16 @@ "inputs": [ { "internalType": "address", "name": "takerToken", "type": "address" }, { "internalType": "address", "name": "makerToken", "type": "address" }, - { "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" } + { "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" }, + { + "components": [ + { "internalType": "uint256", "name": "targetSlippageBps", "type": "uint256" }, + { "internalType": "uint256", "name": "maxIterations", "type": "uint256" } + ], + "internalType": "struct IERC20BridgeSampler.FakeBuyOptions", + "name": "opts", + "type": "tuple" + } ], "name": "sampleBuysFromKyberNetwork", "outputs": [{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }], @@ -145,7 +154,16 @@ { "internalType": "address", "name": "registryAddress", "type": "address" }, { "internalType": "address", "name": "takerToken", "type": "address" }, { "internalType": "address", "name": "makerToken", "type": "address" }, - { "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" } + { "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" }, + { + "components": [ + { "internalType": "uint256", "name": "targetSlippageBps", "type": "uint256" }, + { "internalType": "uint256", "name": "maxIterations", "type": "uint256" } + ], + "internalType": "struct IERC20BridgeSampler.FakeBuyOptions", + "name": "opts", + "type": "tuple" + } ], "name": "sampleBuysFromLiquidityProviderRegistry", "outputs": [{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }], @@ -284,20 +302,22 @@ }, "return": "takerTokenAmounts Taker amounts sold at each maker token amount." }, - "sampleBuysFromKyberNetwork(address,address,uint256[])": { + "sampleBuysFromKyberNetwork(address,address,uint256[],(uint256,uint256))": { "details": "Sample buy quotes from Kyber.", "params": { "makerToken": "Address of the maker token (what to buy).", "makerTokenAmounts": "Maker token buy amount for each sample.", + "opts": "`FakeBuyOptions` specifying target slippage and max iterations.", "takerToken": "Address of the taker token (what to sell)." }, "return": "takerTokenAmounts Taker amounts sold at each maker token amount." }, - "sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[])": { + "sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[],(uint256,uint256))": { "details": "Sample buy quotes from an arbitrary on-chain liquidity provider.", "params": { "makerToken": "Address of the maker token (what to buy).", "makerTokenAmounts": "Maker token buy amount for each sample.", + "opts": "`FakeBuyOptions` specifying target slippage and max iterations.", "registryAddress": "Address of the liquidity provider registry contract.", "takerToken": "Address of the taker token (what to sell)." }, @@ -363,10 +383,10 @@ }, "evm": { "bytecode": { - "object": "0x60806040523480156200001157600080fd5b50604051620037833803806200378383398101604081905262000034916200005a565b600080546001600160a01b0319166001600160a01b03929092169190911790556200008a565b6000602082840312156200006c578081fd5b81516001600160a01b038116811462000083578182fd5b9392505050565b6136e9806200009a6000396000f3fe608060405234801561001057600080fd5b50600436106100ea5760003560e01c806364ee6ade1161008c5780638b123a02116100665780638b123a02146101d05780639f76ad35146101e3578063a2c28d4b146101f6578063c7f7142e14610209576100ea565b806364ee6ade1461018a57806368be3cf21461019d5780636dd6b78d146101bd576100ea565b80634703a7e6116100c85780634703a7e61461013e5780634cb8e2531461015157806359f515d01461016457806360ee052a14610177576100ea565b80630cc6600b146100ef5780631796fb8714610118578063354152a31461012b575b600080fd5b6101026100fd366004612ff8565b610229565b60405161010f91906133bf565b60405180910390f35b610102610126366004613058565b610944565b610102610139366004613058565b610b15565b61010261014c366004612ff8565b610ccf565b61010261015f366004612ff8565b610eab565b610102610172366004613102565b61119b565b610102610185366004612ff8565b611244565b610102610198366004612ff8565b611504565b6101b06101ab366004613096565b6116ca565b60405161010f9190613341565b6101026101cb366004612ff8565b611809565b6101026101de366004613102565b611ab1565b6101026101f1366004612f85565b611d8a565b610102610204366004612f85565b611f8f565b61021c610217366004612f3b565b612526565b60405161010f91906132c8565b60606102358385612660565b81516102405761093d565b600061024a6126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146102825784610298565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006102a46126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146102dc57846102f2565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006102ff876126eb565b60ff169050600061030f876126eb565b60ff169050855160405190808252806020026020018201604052801561033f578160200160208202803883390190505b50945061034a612c0f565b600060606103566126fc565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b888a8d6000815181106103a157fe5b60200260200101516040516024016103bb93929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161044491906132ac565b6000604051808303818686fa925050503d8060008114610480576040519150601f19603f3d011682016040523d82523d6000602084013e610485565b606091505b509150915081156104ae57808060200190516104a49190810190613220565b60408401526104bb565b5061093d95505050505050565b670de0b6b3a764000084600a0a86600a0a8b6000815181106104d957fe5b602002602001015186604001510202816104ef57fe5b04816104f757fe5b0460208401526105056126fc565b602084015160405173ffffffffffffffffffffffffffffffffffffffff92909216916216e360917f809a9e55000000000000000000000000000000000000000000000000000000009161055f918c918c9190602401613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516105e891906132ac565b6000604051808303818686fa925050503d8060008114610624576040519150601f19603f3d011682016040523d82523d6000602084013e610629565b606091505b50909250905081156104ae57808060200190516106499190810190613220565b6040840152670de0b6b3a764000085600a0a85600a0a8560200151866040015102028161067257fe5b048161067a57fe5b04835260005b895181101561093457600a606085015260005b6106b98b83815181106106a257fe5b602002602001015186600001518760200151612714565b602086018190526106d1906127159061271090612714565b60208601526106de6126fc565b602086015160405173ffffffffffffffffffffffffffffffffffffffff92909216916216e360917f809a9e550000000000000000000000000000000000000000000000000000000091610738918e918e9190602401613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516107c191906132ac565b6000604051808303818686fa925050503d80600081146107fd576040519150601f19603f3d011682016040523d82523d6000602084013e610802565b606091505b509094509250831561082c57828060200190516108229190810190613220565b6040860152610831565b610905565b670de0b6b3a764000087600a0a87600a0a8760200151886040015102028161085557fe5b048161085d57fe5b0485528a518b908390811061086e57fe5b60200260200101518560000151106108c7576127108b838151811061088f57fe5b60200260200101518161089e57fe5b048b83815181106108ab57fe5b6020026020010151866000015103816108c057fe5b0460608601525b8a82815181106108d357fe5b602002602001015185600001511080156108f1575060058560600151115b80156109005750600101600581105b610693575b6109148b83815181106106a257fe5b8a838151811061092057fe5b602090810291909101015250600101610680565b50505050505050505b9392505050565b606060008251905080604051908082528060200260200182016040528015610976578160200160208202803883390190505b50915060005b81811015610b0b57600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff16630e71d1b9905060e01b8a8a8a88815181106109d357fe5b60200260200101516040516024016109ed93929190613432565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610a7691906132ac565b6000604051808303818686fa925050503d8060008114610ab2576040519150601f19603f3d011682016040523d82523d6000602084013e610ab7565b606091505b50909250905060008215610ae05781806020019051610ad99190810190613220565b9050610ae8565b505050610b0b565b80868581518110610af557fe5b602090810291909101015250505060010161097c565b5050949350505050565b606060008251905080604051908082528060200260200182016040528015610b47578160200160208202803883390190505b50915060005b81811015610b0b57600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a8881518110610ba457fe5b6020026020010151604051602401610bbe93929190613432565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610c4791906132ac565b6000604051808303818686fa925050503d8060008114610c83576040519150601f19603f3d011682016040523d82523d6000602084013e610c88565b606091505b50909250905060008215610ae05781806020019051610caa9190810190613220565b905080868581518110610cb957fe5b6020908102919091010152505050600101610b4d565b6060610cdb8385612660565b8151604080518281526020808402820101909152818015610d06578160200160208202803883390190505b50915060005b81811015610ea25760006060610d20612756565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a8881518110610d6a57fe5b6020026020010151604051602401610d8493929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610e0d91906132ac565b6000604051808303818686fa925050503d8060008114610e49576040519150601f19603f3d011682016040523d82523d6000602084013e610e4e565b606091505b50909250905060008215610e775781806020019051610e709190810190613220565b9050610e7f565b505050610ea2565b80868581518110610e8c57fe5b6020908102919091010152505050600101610d0c565b50509392505050565b6060610eb78385612660565b6000610ec16126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614610ef95784610f0f565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b90506000610f1b6126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614610f535784610f69565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b90506000610f76876126eb565b60ff1690506000610f86876126eb565b60ff16905060008651905080604051908082528060200260200182016040528015610fbb578160200160208202803883390190505b50955060005b8181101561118e5760006060610fd56126fc565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b8a8a8e888151811061101f57fe5b602002602001015160405160240161103993929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516110c291906132ac565b6000604051808303818686fa925050503d80600081146110fe576040519150601f19603f3d011682016040523d82523d6000602084013e611103565b606091505b5090925090506000821561112c57818060200190516111259190810190613220565b9050611134565b50505061118e565b670de0b6b3a764000087600a0a87600a0a8d878151811061115157fe5b60200260200101518402028161116357fe5b048161116b57fe5b048a858151811061117857fe5b6020908102919091010152505050600101610fc1565b5050505050509392505050565b60606111a78383611ab1565b905060005b835181101561123d578181815181106111c157fe5b60200260200101516000146112355761121c8282815181106111df57fe5b60200260200101518583815181106111f357fe5b602002602001015160a0015186848151811061120b57fe5b602002602001015160800151612714565b82828151811061122857fe5b6020026020010181815250505b6001016111ac565b5092915050565b60606112508385612660565b815160408051828152602080840282010190915281801561127b578160200160208202803883390190505b50915060006112886126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146112c8576112c38661276e565b6112cb565b60005b905060006112d76126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614611317576113128661276e565b61131a565b60005b905060005b838110156114f95760016113316126d3565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614156113c85786516113a79085907f2640f62c00000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b6020026020010151612800565b8784815181106113b357fe5b602002602001018193508281525050506114e5565b6113d06126d3565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156114395786516113a79084907f59e9486200000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b86516000906114729085907f59e9486200000000000000000000000000000000000000000000000000000000908b908790811061139a57fe5b9250905080156114c8576114a7857f2640f62c0000000000000000000000000000000000000000000000000000000083612800565b8885815181106114b357fe5b602002602001018194508281525050506114e3565b60008784815181106114d657fe5b6020026020010181815250505b505b806114f057506114f9565b5060010161131f565b505050509392505050565b60606115108385612660565b815160408051828152602080840282010190915281801561153b578160200160208202803883390190505b50915060005b81811015610ea25760006060611555612756565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a888151811061159f57fe5b60200260200101516040516024016115b993929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161164291906132ac565b6000604051808303818686fa925050503d806000811461167e576040519150601f19603f3d011682016040523d82523d6000602084013e611683565b606091505b50909250905060008215610e7757818060200190516116a59190810190613220565b9050808685815181106116b457fe5b6020908102919091010152505050600101611541565b60408051828152602080840282010190915260609082801561170057816020015b60608152602001906001900390816116eb5790505b50905060005b80831461123d57600060603086868581811061171e57fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13684900301811261175857600080fd5b9091016020810191503567ffffffffffffffff81111561177757600080fd5b3681900382131561178757600080fd5b60405161179592919061329c565b600060405180830381855afa9150503d80600081146117d0576040519150601f19603f3d011682016040523d82523d6000602084013e6117d5565b606091505b5091509150816117e757805160208201fd5b808484815181106117f457fe5b60209081029190910101525050600101611706565b60606118158385612660565b8151604080518281526020808402820101909152818015611840578160200160208202803883390190505b509150600061184d6126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161461188d576118888661276e565b611890565b60005b9050600061189c6126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146118dc576118d78661276e565b6118df565b60005b905060005b838110156114f95760016118f66126d3565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16141561198057865161195f9085907f95b68fe700000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b87848151811061196b57fe5b60200260200101819350828152505050611a9d565b6119886126d3565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156119f157865161195f9084907fcd7724c300000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b8651600090611a2a9086907f95b68fe700000000000000000000000000000000000000000000000000000000908b908790811061139a57fe5b925090508015611a8057611a5f847fcd7724c30000000000000000000000000000000000000000000000000000000083612800565b888581518110611a6b57fe5b60200260200101819450828152505050611a9b565b6000878481518110611a8e57fe5b6020026020010181815250505b505b80611aa857506114f9565b506001016118e4565b60608251604051908082528060200260200182016040528015611ade578160200160208202803883390190505b506000805491925073ffffffffffffffffffffffffffffffffffffffff909116905b84518114611d8257838181518110611b1457fe5b60200260200101515160001480611b425750848181518110611b3257fe5b6020026020010151608001516000145b80611b645750848181518110611b5457fe5b602002602001015160a001516000145b15611b88576000838281518110611b7757fe5b602002602001018181525050611d7a565b600060608373ffffffffffffffffffffffffffffffffffffffff166207a1208573ffffffffffffffffffffffffffffffffffffffff1663e77286eb905060e01b898681518110611bd457fe5b6020026020010151898781518110611be857fe5b6020026020010151604051602401611c019291906134ad565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611c8a91906132ac565b6000604051808303818686fa925050503d8060008114611cc6576040519150601f19603f3d011682016040523d82523d6000602084013e611ccb565b606091505b509150915081611cf6576000858481518110611ce357fe5b6020026020010181815250505050611d7a565b611cfe612c37565b60008083806020019051611d1591908101906131b3565b91945092509050600383516006811115611d2b57fe5b141580611d36575080155b15611d5a576000888781518110611d4957fe5b602002602001018181525050611d74565b81888781518110611d6757fe5b6020026020010181815250505b50505050505b600101611b00565b505092915050565b606060008251905080604051908082528060200260200182016040528015611dbc578160200160208202803883390190505b5091506000611dcc878787612526565b905073ffffffffffffffffffffffffffffffffffffffff8116611df15750611f879050565b60005b82811015611f8357600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b8881518110611e4b57fe5b6020026020010151604051602401611e6593929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611eee91906132ac565b6000604051808303818686fa925050503d8060008114611f2a576040519150601f19603f3d011682016040523d82523d6000602084013e611f2f565b606091505b50909250905060008215611f585781806020019051611f519190810190613220565b9050611f60565b505050611f83565b80878581518110611f6d57fe5b6020908102919091010152505050600101611df4565b5050505b949350505050565b606060008251905080604051908082528060200260200182016040528015611fc1578160200160208202803883390190505b5091506000611fd1878787612526565b905073ffffffffffffffffffffffffffffffffffffffff81161580611ff4575081155b156120015750611f879050565b612009612c0f565b600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8a8c8b60008151811061205957fe5b602002602001015160405160240161207393929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516120fc91906132ac565b6000604051808303818686fa925050503d8060008114612138576040519150601f19603f3d011682016040523d82523d6000602084013e61213d565b606091505b50915091508115612166578080602001905161215c9190810190613220565b6020840152612171565b50611f879350505050565b602083015160405173ffffffffffffffffffffffffffffffffffffffff86169162030d40917f343fbcdd00000000000000000000000000000000000000000000000000000000916121c8918e918e91602401613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161225191906132ac565b6000604051808303818686fa925050503d806000811461228d576040519150601f19603f3d011682016040523d82523d6000602084013e612292565b606091505b509092509050811561216657808060200190516122b29190810190613220565b835260005b875181101561251857600a606085015260005b6122d98983815181106106a257fe5b602086018190526122f1906127159061271090612714565b8560200181815250508573ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8d8d896020015160405160240161235093929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516123d991906132ac565b6000604051808303818686fa925050503d8060008114612415576040519150601f19603f3d011682016040523d82523d6000602084013e61241a565b606091505b5090945092508315612441578280602001905161243a9190810190613220565b8552612446565b6124e9565b88828151811061245257fe5b60200260200101518560000151106124ab5761271089838151811061247357fe5b60200260200101518161248257fe5b0489838151811061248f57fe5b6020026020010151866000015103816124a457fe5b0460608601525b8882815181106124b757fe5b602002602001015185600001511080156124d5575060058560600151115b80156124e45750600101600581105b6122ca575b6124f88983815181106106a257fe5b88838151811061250457fe5b6020908102919091010152506001016122b7565b505050505050949350505050565b6040516000906060907f153f5997000000000000000000000000000000000000000000000000000000009061256190869086906024016132e9565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff16836040516125e991906132ac565b600060405180830381855afa9150503d8060008114612624576040519150601f19603f3d011682016040523d82523d6000602084013e612629565b606091505b509150915081801561263c575080516020145b156126565761264c81600c612949565b935050505061093d565b5050509392505050565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156126cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016126c690613450565b60405180910390fd5b5050565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b60006126f68261298e565b92915050565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b6000611f878361274a61272e82600163ffffffff612a5f16565b61273e888763ffffffff612a7e16565b9063ffffffff612aaf16565b9063ffffffff612acb16565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b6000612778612af5565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b81526004016127b091906132c8565b60206040518083038186803b1580156127c857600080fd5b505afa1580156127dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126f69190810190612f1f565b60008073ffffffffffffffffffffffffffffffffffffffff851661282357612941565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f086866040516024016128529190613601565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516128db91906132ac565b6000604051808303818686fa925050503d8060008114612917576040519150601f19603f3d011682016040523d82523d6000602084013e61291c565b606091505b509092509050811561293f578080602001905161293c9190810190613220565b92505b505b935093915050565b6000816014018351101561296f5761296f61296a6004855185601401612b0d565b612bb2565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce567000000000000000000000000000000000000000000000000000000008152506040516129f291906132ac565b600060405180830381855afa9150503d8060008114612a2d576040519150601f19603f3d011682016040523d82523d6000602084013e612a32565b606091505b5091509150818015612a45575080516020145b15612a5857612a55816000612bba565b92505b5050919050565b600082821115612a7857612a7861296a60028585612bc6565b50900390565b600082612a8d575060006126f6565b82820282848281612a9a57fe5b041461093d5761093d61296a60018686612bc6565b60008282018381101561093d5761093d61296a60008686612bc6565b600081612ae157612ae161296a60038585612bc6565b6000828481612aec57fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b6060632800659560e01b848484604051602401612b2c93929190613424565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b600061093d8383612be5565b606063e946c1bb60e01b848484604051602401612b2c93929190613402565b60008160200183511015612c0657612c0661296a6005855185602001612b0d565b50016020015190565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b6040805160608101909152806000815260006020820181905260409091015290565b80356126f681613681565b600082601f830112612c74578081fd5b8135612c87612c8282613631565b61360a565b8181529150602080830190840160005b83811015612cc457612caf8760208435890101612d37565b83526020928301929190910190600101612c97565b5050505092915050565b600082601f830112612cde578081fd5b8135612cec612c8282613631565b818152915060208083019084810181840286018201871015612d0d57600080fd5b60005b84811015612d2c57813584529282019290820190600101612d10565b505050505092915050565b600082601f830112612d47578081fd5b813567ffffffffffffffff811115612d5d578182fd5b612d8e60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161360a565b9150808252836020828501011115612da557600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b81146126f657600080fd5b60006101c0808385031215612de3578182fd5b612dec8161360a565b915050612df98383612c59565b8152612e088360208401612c59565b6020820152612e1a8360408401612c59565b6040820152612e2c8360608401612c59565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff80821115612e8e57600080fd5b612e9a86838701612d37565b83850152610160925082850135915080821115612eb657600080fd5b612ec286838701612d37565b83850152610180925082850135915080821115612ede57600080fd5b612eea86838701612d37565b838501526101a0925082850135915080821115612f0657600080fd5b50612f1385828601612d37565b82840152505092915050565b600060208284031215612f30578081fd5b815161093d81613681565b600080600060608486031215612f4f578182fd5b8335612f5a81613681565b92506020840135612f6a81613681565b91506040840135612f7a81613681565b809150509250925092565b60008060008060808587031215612f9a578081fd5b8435612fa581613681565b93506020850135612fb581613681565b92506040850135612fc581613681565b9150606085013567ffffffffffffffff811115612fe0578182fd5b612fec87828801612cce565b91505092959194509250565b60008060006060848603121561300c578283fd5b833561301781613681565b9250602084013561302781613681565b9150604084013567ffffffffffffffff811115613042578182fd5b61304e86828701612cce565b9150509250925092565b6000806000806080858703121561306d578182fd5b843561307881613681565b93506130878660208701612dbe565b9250612fc58660408701612dbe565b600080602083850312156130a8578182fd5b823567ffffffffffffffff808211156130bf578384fd5b81850186601f8201126130d0578485fd5b80359250818311156130e0578485fd5b86602080850283010111156130f3578485fd5b60200196919550909350505050565b60008060408385031215613114578182fd5b823567ffffffffffffffff8082111561312b578384fd5b81850186601f82011261313c578485fd5b8035925061314c612c8284613631565b83815260208082019190838101885b87811015613184576131728c848435890101612dd0565b8552938201939082019060010161315b565b5091975088013594505050508082111561319c578283fd5b506131a985828601612c64565b9150509250929050565b600080600083850360a08112156131c8578182fd5b60608112156131d5578182fd5b506131e0606061360a565b8451600781106131ee578283fd5b815260208581015190820152604080860151908201526060850151608086015191945092508015158114612f7a578182fd5b600060208284031215613231578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845261326a816020860160208601613651565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b600082516132be818460208701613651565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b6000602080830181845280855180835260408601915060408482028701019250838701855b828110156133b2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526133a0858351613252565b94509285019290850190600101613366565b5092979650505050505050565b602080825282518282018190526000918401906040840190835b818110156133f75783518352602093840193909201916001016133d9565b509095945050505050565b606081016004851061341057fe5b938152602081019290925260409091015290565b606081016008851061341057fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b6000604082526134c1604083018551613238565b60208401516134d36060840182613238565b5060408401516134e66080840182613238565b5060608401516134f960a0840182613238565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c09150610180828187015261356a610200870185613252565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a08388830301818901526135a98287613252565b838b015196508489820301868a01526135c28188613252565b955050808a0151955050505080858303016101e0860152506135e48183613252565b84810360208601526135f68187613252565b979650505050505050565b90815260200190565b60405181810167ffffffffffffffff8111828210171561362957600080fd5b604052919050565b600067ffffffffffffffff821115613647578081fd5b5060209081020190565b60005b8381101561366c578181015183820152602001613654565b8381111561367b576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff811681146136a357600080fd5b5056fea365627a7a7231582001492aae0645e3694f9346a9de400473886ee87e4ce809789ba8b68683a5f2876c6578706572696d656e74616cf564736f6c63430005110040" + "object": "0x60806040523480156200001157600080fd5b50604051620031de380380620031de83398101604081905262000034916200005a565b600080546001600160a01b0319166001600160a01b03929092169190911790556200008a565b6000602082840312156200006c578081fd5b81516001600160a01b038116811462000083578182fd5b9392505050565b613144806200009a6000396000f3fe608060405234801561001057600080fd5b50600436106100ea5760003560e01c806368be3cf21161008c57806398cdafba1161006657806398cdafba146101d05780639f76ad35146101e3578063c7f7142e146101f6578063d0eea06d14610216576100ea565b806368be3cf21461018a5780636dd6b78d146101aa5780638b123a02146101bd576100ea565b80634cb8e253116100c85780634cb8e2531461013e57806359f515d01461015157806360ee052a1461016457806364ee6ade14610177576100ea565b80631796fb87146100ef578063354152a3146101185780634703a7e61461012b575b600080fd5b6101026100fd36600461299b565b610229565b60405161010f9190612e4a565b60405180910390f35b61010261012636600461299b565b6103fa565b6101026101393660046128c9565b6105b4565b61010261014c3660046128c9565b610790565b61010261015f366004612a45565b610a80565b6101026101723660046128c9565b610b29565b6101026101853660046128c9565b610de9565b61019d6101983660046129d9565b610faf565b60405161010f9190612dcc565b6101026101b83660046128c9565b6110ee565b6101026101cb366004612a45565b611396565b6101026101de366004612844565b61166f565b6101026101f13660046127d1565b6116ab565b610209610204366004612787565b6118b0565b60405161010f9190612cd9565b610102610224366004612929565b6119eb565b60606000825190508060405190808252806020026020018201604052801561025b578160200160208202803883390190505b50915060005b818110156103f057600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff16630e71d1b9905060e01b8a8a8a88815181106102b857fe5b60200260200101516040516024016102d293929190612e8d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161035b9190612cbd565b6000604051808303818686fa925050503d8060008114610397576040519150601f19603f3d011682016040523d82523d6000602084013e61039c565b606091505b509092509050600082156103c557818060200190516103be9190810190612bf6565b90506103cd565b5050506103f0565b808685815181106103da57fe5b6020908102919091010152505050600101610261565b5050949350505050565b60606000825190508060405190808252806020026020018201604052801561042c578160200160208202803883390190505b50915060005b818110156103f057600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a888151811061048957fe5b60200260200101516040516024016104a393929190612e8d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161052c9190612cbd565b6000604051808303818686fa925050503d8060008114610568576040519150601f19603f3d011682016040523d82523d6000602084013e61056d565b606091505b509092509050600082156103c5578180602001905161058f9190810190612bf6565b90508086858151811061059e57fe5b6020908102919091010152505050600101610432565b60606105c08385611a1c565b81516040805182815260208084028201019091528180156105eb578160200160208202803883390190505b50915060005b818110156107875760006060610605611a8f565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a888151811061064f57fe5b602002602001015160405160240161066993929190612d9b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516106f29190612cbd565b6000604051808303818686fa925050503d806000811461072e576040519150601f19603f3d011682016040523d82523d6000602084013e610733565b606091505b5090925090506000821561075c57818060200190516107559190810190612bf6565b9050610764565b505050610787565b8086858151811061077157fe5b60209081029190910101525050506001016105f1565b50509392505050565b606061079c8385611a1c565b60006107a6611aa7565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146107de57846107f4565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b90506000610800611aa7565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614610838578461084e565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b9050600061085b87611abf565b60ff169050600061086b87611abf565b60ff169050600086519050806040519080825280602002602001820160405280156108a0578160200160208202803883390190505b50955060005b81811015610a7357600060606108ba611ad0565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b8a8a8e888151811061090457fe5b602002602001015160405160240161091e93929190612d9b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516109a79190612cbd565b6000604051808303818686fa925050503d80600081146109e3576040519150601f19603f3d011682016040523d82523d6000602084013e6109e8565b606091505b50909250905060008215610a115781806020019051610a0a9190810190612bf6565b9050610a19565b505050610a73565b670de0b6b3a764000087600a0a87600a0a8d8781518110610a3657fe5b602002602001015184020281610a4857fe5b0481610a5057fe5b048a8581518110610a5d57fe5b60209081029190910101525050506001016108a6565b5050505050509392505050565b6060610a8c8383611396565b905060005b8351811015610b2257818181518110610aa657fe5b6020026020010151600014610b1a57610b01828281518110610ac457fe5b6020026020010151858381518110610ad857fe5b602002602001015160a00151868481518110610af057fe5b602002602001015160800151611ae8565b828281518110610b0d57fe5b6020026020010181815250505b600101610a91565b5092915050565b6060610b358385611a1c565b8151604080518281526020808402820101909152818015610b60578160200160208202803883390190505b5091506000610b6d611aa7565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610bad57610ba886611b2a565b610bb0565b60005b90506000610bbc611aa7565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610bfc57610bf786611b2a565b610bff565b60005b905060005b83811015610dde576001610c16611aa7565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415610cad578651610c8c9085907f2640f62c00000000000000000000000000000000000000000000000000000000908a9086908110610c7f57fe5b6020026020010151611bbc565b878481518110610c9857fe5b60200260200101819350828152505050610dca565b610cb5611aa7565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415610d1e578651610c8c9084907f59e9486200000000000000000000000000000000000000000000000000000000908a9086908110610c7f57fe5b8651600090610d579085907f59e9486200000000000000000000000000000000000000000000000000000000908b9087908110610c7f57fe5b925090508015610dad57610d8c857f2640f62c0000000000000000000000000000000000000000000000000000000083611bbc565b888581518110610d9857fe5b60200260200101819450828152505050610dc8565b6000878481518110610dbb57fe5b6020026020010181815250505b505b80610dd55750610dde565b50600101610c04565b505050509392505050565b6060610df58385611a1c565b8151604080518281526020808402820101909152818015610e20578160200160208202803883390190505b50915060005b818110156107875760006060610e3a611a8f565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a8881518110610e8457fe5b6020026020010151604051602401610e9e93929190612d9b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610f279190612cbd565b6000604051808303818686fa925050503d8060008114610f63576040519150601f19603f3d011682016040523d82523d6000602084013e610f68565b606091505b5090925090506000821561075c5781806020019051610f8a9190810190612bf6565b905080868581518110610f9957fe5b6020908102919091010152505050600101610e26565b604080518281526020808402820101909152606090828015610fe557816020015b6060815260200190600190039081610fd05790505b50905060005b808314610b2257600060603086868581811061100357fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13684900301811261103d57600080fd5b9091016020810191503567ffffffffffffffff81111561105c57600080fd5b3681900382131561106c57600080fd5b60405161107a929190612cad565b600060405180830381855afa9150503d80600081146110b5576040519150601f19603f3d011682016040523d82523d6000602084013e6110ba565b606091505b5091509150816110cc57805160208201fd5b808484815181106110d957fe5b60209081029190910101525050600101610feb565b60606110fa8385611a1c565b8151604080518281526020808402820101909152818015611125578160200160208202803883390190505b5091506000611132611aa7565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146111725761116d86611b2a565b611175565b60005b90506000611181611aa7565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146111c1576111bc86611b2a565b6111c4565b60005b905060005b83811015610dde5760016111db611aa7565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614156112655786516112449085907f95b68fe700000000000000000000000000000000000000000000000000000000908a9086908110610c7f57fe5b87848151811061125057fe5b60200260200101819350828152505050611382565b61126d611aa7565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156112d65786516112449084907fcd7724c300000000000000000000000000000000000000000000000000000000908a9086908110610c7f57fe5b865160009061130f9086907f95b68fe700000000000000000000000000000000000000000000000000000000908b9087908110610c7f57fe5b92509050801561136557611344847fcd7724c30000000000000000000000000000000000000000000000000000000083611bbc565b88858151811061135057fe5b60200260200101819450828152505050611380565b600087848151811061137357fe5b6020026020010181815250505b505b8061138d5750610dde565b506001016111c9565b606082516040519080825280602002602001820160405280156113c3578160200160208202803883390190505b506000805491925073ffffffffffffffffffffffffffffffffffffffff909116905b84518114611667578381815181106113f957fe5b60200260200101515160001480611427575084818151811061141757fe5b6020026020010151608001516000145b80611449575084818151811061143957fe5b602002602001015160a001516000145b1561146d57600083828151811061145c57fe5b60200260200101818152505061165f565b600060608373ffffffffffffffffffffffffffffffffffffffff166207a1208573ffffffffffffffffffffffffffffffffffffffff1663e77286eb905060e01b8986815181106114b957fe5b60200260200101518987815181106114cd57fe5b60200260200101516040516024016114e6929190612f08565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161156f9190612cbd565b6000604051808303818686fa925050503d80600081146115ab576040519150601f19603f3d011682016040523d82523d6000602084013e6115b0565b606091505b5091509150816115db5760008584815181106115c857fe5b602002602001018181525050505061165f565b6115e3612452565b600080838060200190516115fa9190810190612b89565b9194509250905060038351600681111561161057fe5b14158061161b575080155b1561163f57600088878151811061162e57fe5b602002602001018181525050611659565b8188878151811061164c57fe5b6020026020010181815250505b50505050505b6001016113e5565b505092915050565b606061169f858585857f9f76ad35000000000000000000000000000000000000000000000000000000008b611d05565b90505b95945050505050565b6060600082519050806040519080825280602002602001820160405280156116dd578160200160208202803883390190505b50915060006116ed8787876118b0565b905073ffffffffffffffffffffffffffffffffffffffff811661171257506118a89050565b60005b828110156118a457600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b888151811061176c57fe5b602002602001015160405160240161178693929190612d9b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161180f9190612cbd565b6000604051808303818686fa925050503d806000811461184b576040519150601f19603f3d011682016040523d82523d6000602084013e611850565b606091505b5090925090506000821561187957818060200190516118729190810190612bf6565b9050611881565b5050506118a4565b8087858151811061188e57fe5b6020908102919091010152505050600101611715565b5050505b949350505050565b6040516000906060907f153f599700000000000000000000000000000000000000000000000000000000906118eb9086908690602401612cfa565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff16836040516119739190612cbd565b600060405180830381855afa9150503d80600081146119ae576040519150601f19603f3d011682016040523d82523d6000602084013e6119b3565b606091505b50915091508180156119c6575080516020145b156119e0576119d681600c611ed7565b93505050506119e4565b5050505b9392505050565b60606116a2858585857f4cb8e253000000000000000000000000000000000000000000000000000000006000611d05565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611a8b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a8290612eab565b60405180910390fd5b5050565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b6000611aca82611f1c565b92915050565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b60006118a883611b1e611b0282600163ffffffff611fed16565b611b12888763ffffffff61200c16565b9063ffffffff61203d16565b9063ffffffff61205916565b6000611b34612083565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b8152600401611b6c9190612cd9565b60206040518083038186803b158015611b8457600080fd5b505afa158015611b98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611aca919081019061276b565b60008073ffffffffffffffffffffffffffffffffffffffff8516611bdf57611cfd565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f08686604051602401611c0e919061305c565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611c979190612cbd565b6000604051808303818686fa925050503d8060008114611cd3576040519150601f19603f3d011682016040523d82523d6000602084013e611cd8565b606091505b5090925090508115611cfb5780806020019051611cf89190810190612bf6565b92505b505b935093915050565b6060611d118688611a1c565b8451611d1c57611ecd565b60008060008751604051908082528060200260200182016040528015611d4c578160200160208202803883390190505b509350611d70898b8a600081518110611d6157fe5b6020026020010151898961209b565b925082611d805750611ecd915050565b611d8d8a8a85898961209b565b915081611d9d5750611ecd915050565b60005b8851811015611ec8578751600101915060005b611dd18a8381518110611dc257fe5b60200260200101518587611ae8565b9450611de889600001516127100161271087611ae8565b9450611df78c8c878b8b61209b565b935083611e0357611e99565b898281518110611e0f57fe5b60200260200101518410611e5d576127108a8381518110611e2c57fe5b602002602001015181611e3b57fe5b048a8381518110611e4857fe5b6020026020010151850381611e5957fe5b0492505b898281518110611e6957fe5b602002602001015184108015611e7f5750885183115b8015611e945750886020015181600101915081105b611db3575b611ea88a8381518110611dc257fe5b868381518110611eb457fe5b602090810291909101015250600101611da0565b505050505b9695505050505050565b60008160140183511015611efd57611efd611ef86004855185601401612350565b6123f5565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce56700000000000000000000000000000000000000000000000000000000815250604051611f809190612cbd565b600060405180830381855afa9150503d8060008114611fbb576040519150601f19603f3d011682016040523d82523d6000602084013e611fc0565b606091505b5091509150818015611fd3575080516020145b15611fe657611fe38160006123fd565b92505b5050919050565b60008282111561200657612006611ef860028585612409565b50900390565b60008261201b57506000611aca565b8282028284828161202857fe5b04146119e4576119e4611ef860018686612409565b6000828201838110156119e4576119e4611ef860008686612409565b60008161206f5761206f611ef860038585612409565b600082848161207a57fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b60408051600180825281830190925260009160609182916020808301908038833901905050905085816000815181106120d057fe5b60209081029190910101527fffffffff0000000000000000000000000000000000000000000000000000000085167f4cb8e2530000000000000000000000000000000000000000000000000000000014156121e0576040517f4cb8e253000000000000000000000000000000000000000000000000000000009061215c908a908a908590602401612d62565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529150612299565b6040517f9f76ad3500000000000000000000000000000000000000000000000000000000906122199086908b908b908690602401612d21565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915291505b600060603073ffffffffffffffffffffffffffffffffffffffff16846040516122c29190612cbd565b600060405180830381855afa9150503d80600081146122fd576040519150601f19603f3d011682016040523d82523d6000602084013e612302565b606091505b5091509150816123195760009450505050506116a2565b8080602001905161232d9190810190612af6565b60008151811061233957fe5b602002602001015194505050505095945050505050565b6060632800659560e01b84848460405160240161236f93929190612e7f565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b60006119e48383612428565b606063e946c1bb60e01b84848460405160240161236f93929190612e5d565b6000816020018351101561244957612449611ef86005855185602001612350565b50016020015190565b6040805160608101909152806000815260006020820181905260409091015290565b8035611aca816130dc565b600082601f83011261248f578081fd5b81356124a261249d8261308c565b613065565b8181529150602080830190840160005b838110156124df576124ca8760208435890101612552565b835260209283019291909101906001016124b2565b5050505092915050565b600082601f8301126124f9578081fd5b813561250761249d8261308c565b81815291506020808301908481018184028601820187101561252857600080fd5b60005b848110156125475781358452928201929082019060010161252b565b505050505092915050565b600082601f830112612562578081fd5b813567ffffffffffffffff811115612578578182fd5b6125a960207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613065565b91508082528360208285010111156125c057600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b8114611aca57600080fd5b6000604082840312156125fc578081fd5b6126066040613065565b9050813581526020820135602082015292915050565b60006101c080838503121561262f578182fd5b61263881613065565b9150506126458383612474565b81526126548360208401612474565b60208201526126668360408401612474565b60408201526126788360608401612474565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff808211156126da57600080fd5b6126e686838701612552565b8385015261016092508285013591508082111561270257600080fd5b61270e86838701612552565b8385015261018092508285013591508082111561272a57600080fd5b61273686838701612552565b838501526101a092508285013591508082111561275257600080fd5b5061275f85828601612552565b82840152505092915050565b60006020828403121561277c578081fd5b81516119e4816130dc565b60008060006060848603121561279b578182fd5b83356127a6816130dc565b925060208401356127b6816130dc565b915060408401356127c6816130dc565b809150509250925092565b600080600080608085870312156127e6578081fd5b84356127f1816130dc565b93506020850135612801816130dc565b92506040850135612811816130dc565b9150606085013567ffffffffffffffff81111561282c578182fd5b612838878288016124e9565b91505092959194509250565b600080600080600060c0868803121561285b578283fd5b8535612866816130dc565b94506020860135612876816130dc565b93506040860135612886816130dc565b9250606086013567ffffffffffffffff8111156128a1578182fd5b6128ad888289016124e9565b9250506128bd87608088016125eb565b90509295509295909350565b6000806000606084860312156128dd578081fd5b83356128e8816130dc565b925060208401356128f8816130dc565b9150604084013567ffffffffffffffff811115612913578182fd5b61291f868287016124e9565b9150509250925092565b60008060008060a0858703121561293e578182fd5b8435612949816130dc565b93506020850135612959816130dc565b9250604085013567ffffffffffffffff811115612974578283fd5b612980878288016124e9565b92505061299086606087016125eb565b905092959194509250565b600080600080608085870312156129b0578182fd5b84356129bb816130dc565b93506129ca86602087016125d9565b925061281186604087016125d9565b600080602083850312156129eb578182fd5b823567ffffffffffffffff80821115612a02578384fd5b81850186601f820112612a13578485fd5b8035925081831115612a23578485fd5b8660208085028301011115612a36578485fd5b60200196919550909350505050565b60008060408385031215612a57578182fd5b823567ffffffffffffffff80821115612a6e578384fd5b81850186601f820112612a7f578485fd5b80359250612a8f61249d8461308c565b83815260208082019190838101885b87811015612ac757612ab58c84843589010161261c565b85529382019390820190600101612a9e565b50919750880135945050505080821115612adf578283fd5b50612aec8582860161247f565b9150509250929050565b60006020808385031215612b08578182fd5b825167ffffffffffffffff811115612b1e578283fd5b80840185601f820112612b2f578384fd5b80519150612b3f61249d8361308c565b8281528381019082850185850284018601891015612b5b578687fd5b8693505b84841015612b7d578051835260019390930192918501918501612b5f565b50979650505050505050565b600080600083850360a0811215612b9e578182fd5b6060811215612bab578182fd5b50612bb66060613065565b845160078110612bc4578283fd5b8152602085810151908201526040808601519082015260608501516080860151919450925080151581146127c6578182fd5b600060208284031215612c07578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845260208401935060208301825b82811015612c59578151865260209586019590910190600101612c3b565b5093949350505050565b60008151808452612c7b8160208601602086016130ac565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b60008251612ccf8184602087016130ac565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401528085166040840152506080606083015261169f6080830184612c28565b600073ffffffffffffffffffffffffffffffffffffffff8086168352808516602084015250606060408301526116a26060830184612c28565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b6000602080830181845280855180835260408601915060408482028701019250838701855b82811015612e3d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452612e2b858351612c63565b94509285019290850190600101612df1565b5092979650505050505050565b6000602082526119e46020830184612c28565b6060810160048510612e6b57fe5b938152602081019290925260409091015290565b6060810160088510612e6b57fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b600060408252612f1c604083018551612c0e565b6020840151612f2e6060840182612c0e565b506040840151612f416080840182612c0e565b506060840151612f5460a0840182612c0e565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c091506101808281870152612fc5610200870185612c63565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a08388830301818901526130048287612c63565b838b015196508489820301868a015261301d8188612c63565b955050808a0151955050505080858303016101e08601525061303f8183612c63565b84810360208601526130518187612c63565b979650505050505050565b90815260200190565b60405181810167ffffffffffffffff8111828210171561308457600080fd5b604052919050565b600067ffffffffffffffff8211156130a2578081fd5b5060209081020190565b60005b838110156130c75781810151838201526020016130af565b838111156130d6576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff811681146130fe57600080fd5b5056fea365627a7a723158208777a7f5c90c6021748d69e56d7d4136320b8e8d099fdd1cd108e182d1ee4d7f6c6578706572696d656e74616cf564736f6c63430005110040" }, "deployedBytecode": { - "object": "0x608060405234801561001057600080fd5b50600436106100ea5760003560e01c806364ee6ade1161008c5780638b123a02116100665780638b123a02146101d05780639f76ad35146101e3578063a2c28d4b146101f6578063c7f7142e14610209576100ea565b806364ee6ade1461018a57806368be3cf21461019d5780636dd6b78d146101bd576100ea565b80634703a7e6116100c85780634703a7e61461013e5780634cb8e2531461015157806359f515d01461016457806360ee052a14610177576100ea565b80630cc6600b146100ef5780631796fb8714610118578063354152a31461012b575b600080fd5b6101026100fd366004612ff8565b610229565b60405161010f91906133bf565b60405180910390f35b610102610126366004613058565b610944565b610102610139366004613058565b610b15565b61010261014c366004612ff8565b610ccf565b61010261015f366004612ff8565b610eab565b610102610172366004613102565b61119b565b610102610185366004612ff8565b611244565b610102610198366004612ff8565b611504565b6101b06101ab366004613096565b6116ca565b60405161010f9190613341565b6101026101cb366004612ff8565b611809565b6101026101de366004613102565b611ab1565b6101026101f1366004612f85565b611d8a565b610102610204366004612f85565b611f8f565b61021c610217366004612f3b565b612526565b60405161010f91906132c8565b60606102358385612660565b81516102405761093d565b600061024a6126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146102825784610298565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006102a46126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146102dc57846102f2565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b905060006102ff876126eb565b60ff169050600061030f876126eb565b60ff169050855160405190808252806020026020018201604052801561033f578160200160208202803883390190505b50945061034a612c0f565b600060606103566126fc565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b888a8d6000815181106103a157fe5b60200260200101516040516024016103bb93929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161044491906132ac565b6000604051808303818686fa925050503d8060008114610480576040519150601f19603f3d011682016040523d82523d6000602084013e610485565b606091505b509150915081156104ae57808060200190516104a49190810190613220565b60408401526104bb565b5061093d95505050505050565b670de0b6b3a764000084600a0a86600a0a8b6000815181106104d957fe5b602002602001015186604001510202816104ef57fe5b04816104f757fe5b0460208401526105056126fc565b602084015160405173ffffffffffffffffffffffffffffffffffffffff92909216916216e360917f809a9e55000000000000000000000000000000000000000000000000000000009161055f918c918c9190602401613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516105e891906132ac565b6000604051808303818686fa925050503d8060008114610624576040519150601f19603f3d011682016040523d82523d6000602084013e610629565b606091505b50909250905081156104ae57808060200190516106499190810190613220565b6040840152670de0b6b3a764000085600a0a85600a0a8560200151866040015102028161067257fe5b048161067a57fe5b04835260005b895181101561093457600a606085015260005b6106b98b83815181106106a257fe5b602002602001015186600001518760200151612714565b602086018190526106d1906127159061271090612714565b60208601526106de6126fc565b602086015160405173ffffffffffffffffffffffffffffffffffffffff92909216916216e360917f809a9e550000000000000000000000000000000000000000000000000000000091610738918e918e9190602401613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516107c191906132ac565b6000604051808303818686fa925050503d80600081146107fd576040519150601f19603f3d011682016040523d82523d6000602084013e610802565b606091505b509094509250831561082c57828060200190516108229190810190613220565b6040860152610831565b610905565b670de0b6b3a764000087600a0a87600a0a8760200151886040015102028161085557fe5b048161085d57fe5b0485528a518b908390811061086e57fe5b60200260200101518560000151106108c7576127108b838151811061088f57fe5b60200260200101518161089e57fe5b048b83815181106108ab57fe5b6020026020010151866000015103816108c057fe5b0460608601525b8a82815181106108d357fe5b602002602001015185600001511080156108f1575060058560600151115b80156109005750600101600581105b610693575b6109148b83815181106106a257fe5b8a838151811061092057fe5b602090810291909101015250600101610680565b50505050505050505b9392505050565b606060008251905080604051908082528060200260200182016040528015610976578160200160208202803883390190505b50915060005b81811015610b0b57600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff16630e71d1b9905060e01b8a8a8a88815181106109d357fe5b60200260200101516040516024016109ed93929190613432565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610a7691906132ac565b6000604051808303818686fa925050503d8060008114610ab2576040519150601f19603f3d011682016040523d82523d6000602084013e610ab7565b606091505b50909250905060008215610ae05781806020019051610ad99190810190613220565b9050610ae8565b505050610b0b565b80868581518110610af557fe5b602090810291909101015250505060010161097c565b5050949350505050565b606060008251905080604051908082528060200260200182016040528015610b47578160200160208202803883390190505b50915060005b81811015610b0b57600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a8881518110610ba457fe5b6020026020010151604051602401610bbe93929190613432565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610c4791906132ac565b6000604051808303818686fa925050503d8060008114610c83576040519150601f19603f3d011682016040523d82523d6000602084013e610c88565b606091505b50909250905060008215610ae05781806020019051610caa9190810190613220565b905080868581518110610cb957fe5b6020908102919091010152505050600101610b4d565b6060610cdb8385612660565b8151604080518281526020808402820101909152818015610d06578160200160208202803883390190505b50915060005b81811015610ea25760006060610d20612756565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a8881518110610d6a57fe5b6020026020010151604051602401610d8493929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610e0d91906132ac565b6000604051808303818686fa925050503d8060008114610e49576040519150601f19603f3d011682016040523d82523d6000602084013e610e4e565b606091505b50909250905060008215610e775781806020019051610e709190810190613220565b9050610e7f565b505050610ea2565b80868581518110610e8c57fe5b6020908102919091010152505050600101610d0c565b50509392505050565b6060610eb78385612660565b6000610ec16126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614610ef95784610f0f565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b90506000610f1b6126d3565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614610f535784610f69565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b90506000610f76876126eb565b60ff1690506000610f86876126eb565b60ff16905060008651905080604051908082528060200260200182016040528015610fbb578160200160208202803883390190505b50955060005b8181101561118e5760006060610fd56126fc565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b8a8a8e888151811061101f57fe5b602002602001015160405160240161103993929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516110c291906132ac565b6000604051808303818686fa925050503d80600081146110fe576040519150601f19603f3d011682016040523d82523d6000602084013e611103565b606091505b5090925090506000821561112c57818060200190516111259190810190613220565b9050611134565b50505061118e565b670de0b6b3a764000087600a0a87600a0a8d878151811061115157fe5b60200260200101518402028161116357fe5b048161116b57fe5b048a858151811061117857fe5b6020908102919091010152505050600101610fc1565b5050505050509392505050565b60606111a78383611ab1565b905060005b835181101561123d578181815181106111c157fe5b60200260200101516000146112355761121c8282815181106111df57fe5b60200260200101518583815181106111f357fe5b602002602001015160a0015186848151811061120b57fe5b602002602001015160800151612714565b82828151811061122857fe5b6020026020010181815250505b6001016111ac565b5092915050565b60606112508385612660565b815160408051828152602080840282010190915281801561127b578160200160208202803883390190505b50915060006112886126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146112c8576112c38661276e565b6112cb565b60005b905060006112d76126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614611317576113128661276e565b61131a565b60005b905060005b838110156114f95760016113316126d3565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614156113c85786516113a79085907f2640f62c00000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b6020026020010151612800565b8784815181106113b357fe5b602002602001018193508281525050506114e5565b6113d06126d3565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156114395786516113a79084907f59e9486200000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b86516000906114729085907f59e9486200000000000000000000000000000000000000000000000000000000908b908790811061139a57fe5b9250905080156114c8576114a7857f2640f62c0000000000000000000000000000000000000000000000000000000083612800565b8885815181106114b357fe5b602002602001018194508281525050506114e3565b60008784815181106114d657fe5b6020026020010181815250505b505b806114f057506114f9565b5060010161131f565b505050509392505050565b60606115108385612660565b815160408051828152602080840282010190915281801561153b578160200160208202803883390190505b50915060005b81811015610ea25760006060611555612756565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a888151811061159f57fe5b60200260200101516040516024016115b993929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161164291906132ac565b6000604051808303818686fa925050503d806000811461167e576040519150601f19603f3d011682016040523d82523d6000602084013e611683565b606091505b50909250905060008215610e7757818060200190516116a59190810190613220565b9050808685815181106116b457fe5b6020908102919091010152505050600101611541565b60408051828152602080840282010190915260609082801561170057816020015b60608152602001906001900390816116eb5790505b50905060005b80831461123d57600060603086868581811061171e57fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13684900301811261175857600080fd5b9091016020810191503567ffffffffffffffff81111561177757600080fd5b3681900382131561178757600080fd5b60405161179592919061329c565b600060405180830381855afa9150503d80600081146117d0576040519150601f19603f3d011682016040523d82523d6000602084013e6117d5565b606091505b5091509150816117e757805160208201fd5b808484815181106117f457fe5b60209081029190910101525050600101611706565b60606118158385612660565b8151604080518281526020808402820101909152818015611840578160200160208202803883390190505b509150600061184d6126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161461188d576118888661276e565b611890565b60005b9050600061189c6126d3565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146118dc576118d78661276e565b6118df565b60005b905060005b838110156114f95760016118f66126d3565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16141561198057865161195f9085907f95b68fe700000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b87848151811061196b57fe5b60200260200101819350828152505050611a9d565b6119886126d3565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156119f157865161195f9084907fcd7724c300000000000000000000000000000000000000000000000000000000908a908690811061139a57fe5b8651600090611a2a9086907f95b68fe700000000000000000000000000000000000000000000000000000000908b908790811061139a57fe5b925090508015611a8057611a5f847fcd7724c30000000000000000000000000000000000000000000000000000000083612800565b888581518110611a6b57fe5b60200260200101819450828152505050611a9b565b6000878481518110611a8e57fe5b6020026020010181815250505b505b80611aa857506114f9565b506001016118e4565b60608251604051908082528060200260200182016040528015611ade578160200160208202803883390190505b506000805491925073ffffffffffffffffffffffffffffffffffffffff909116905b84518114611d8257838181518110611b1457fe5b60200260200101515160001480611b425750848181518110611b3257fe5b6020026020010151608001516000145b80611b645750848181518110611b5457fe5b602002602001015160a001516000145b15611b88576000838281518110611b7757fe5b602002602001018181525050611d7a565b600060608373ffffffffffffffffffffffffffffffffffffffff166207a1208573ffffffffffffffffffffffffffffffffffffffff1663e77286eb905060e01b898681518110611bd457fe5b6020026020010151898781518110611be857fe5b6020026020010151604051602401611c019291906134ad565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611c8a91906132ac565b6000604051808303818686fa925050503d8060008114611cc6576040519150601f19603f3d011682016040523d82523d6000602084013e611ccb565b606091505b509150915081611cf6576000858481518110611ce357fe5b6020026020010181815250505050611d7a565b611cfe612c37565b60008083806020019051611d1591908101906131b3565b91945092509050600383516006811115611d2b57fe5b141580611d36575080155b15611d5a576000888781518110611d4957fe5b602002602001018181525050611d74565b81888781518110611d6757fe5b6020026020010181815250505b50505050505b600101611b00565b505092915050565b606060008251905080604051908082528060200260200182016040528015611dbc578160200160208202803883390190505b5091506000611dcc878787612526565b905073ffffffffffffffffffffffffffffffffffffffff8116611df15750611f879050565b60005b82811015611f8357600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b8881518110611e4b57fe5b6020026020010151604051602401611e6593929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611eee91906132ac565b6000604051808303818686fa925050503d8060008114611f2a576040519150601f19603f3d011682016040523d82523d6000602084013e611f2f565b606091505b50909250905060008215611f585781806020019051611f519190810190613220565b9050611f60565b505050611f83565b80878581518110611f6d57fe5b6020908102919091010152505050600101611df4565b5050505b949350505050565b606060008251905080604051908082528060200260200182016040528015611fc1578160200160208202803883390190505b5091506000611fd1878787612526565b905073ffffffffffffffffffffffffffffffffffffffff81161580611ff4575081155b156120015750611f879050565b612009612c0f565b600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8a8c8b60008151811061205957fe5b602002602001015160405160240161207393929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516120fc91906132ac565b6000604051808303818686fa925050503d8060008114612138576040519150601f19603f3d011682016040523d82523d6000602084013e61213d565b606091505b50915091508115612166578080602001905161215c9190810190613220565b6020840152612171565b50611f879350505050565b602083015160405173ffffffffffffffffffffffffffffffffffffffff86169162030d40917f343fbcdd00000000000000000000000000000000000000000000000000000000916121c8918e918e91602401613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161225191906132ac565b6000604051808303818686fa925050503d806000811461228d576040519150601f19603f3d011682016040523d82523d6000602084013e612292565b606091505b509092509050811561216657808060200190516122b29190810190613220565b835260005b875181101561251857600a606085015260005b6122d98983815181106106a257fe5b602086018190526122f1906127159061271090612714565b8560200181815250508573ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8d8d896020015160405160240161235093929190613310565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516123d991906132ac565b6000604051808303818686fa925050503d8060008114612415576040519150601f19603f3d011682016040523d82523d6000602084013e61241a565b606091505b5090945092508315612441578280602001905161243a9190810190613220565b8552612446565b6124e9565b88828151811061245257fe5b60200260200101518560000151106124ab5761271089838151811061247357fe5b60200260200101518161248257fe5b0489838151811061248f57fe5b6020026020010151866000015103816124a457fe5b0460608601525b8882815181106124b757fe5b602002602001015185600001511080156124d5575060058560600151115b80156124e45750600101600581105b6122ca575b6124f88983815181106106a257fe5b88838151811061250457fe5b6020908102919091010152506001016122b7565b505050505050949350505050565b6040516000906060907f153f5997000000000000000000000000000000000000000000000000000000009061256190869086906024016132e9565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff16836040516125e991906132ac565b600060405180830381855afa9150503d8060008114612624576040519150601f19603f3d011682016040523d82523d6000602084013e612629565b606091505b509150915081801561263c575080516020145b156126565761264c81600c612949565b935050505061093d565b5050509392505050565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156126cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016126c690613450565b60405180910390fd5b5050565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b60006126f68261298e565b92915050565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b6000611f878361274a61272e82600163ffffffff612a5f16565b61273e888763ffffffff612a7e16565b9063ffffffff612aaf16565b9063ffffffff612acb16565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b6000612778612af5565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b81526004016127b091906132c8565b60206040518083038186803b1580156127c857600080fd5b505afa1580156127dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126f69190810190612f1f565b60008073ffffffffffffffffffffffffffffffffffffffff851661282357612941565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f086866040516024016128529190613601565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516128db91906132ac565b6000604051808303818686fa925050503d8060008114612917576040519150601f19603f3d011682016040523d82523d6000602084013e61291c565b606091505b509092509050811561293f578080602001905161293c9190810190613220565b92505b505b935093915050565b6000816014018351101561296f5761296f61296a6004855185601401612b0d565b612bb2565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce567000000000000000000000000000000000000000000000000000000008152506040516129f291906132ac565b600060405180830381855afa9150503d8060008114612a2d576040519150601f19603f3d011682016040523d82523d6000602084013e612a32565b606091505b5091509150818015612a45575080516020145b15612a5857612a55816000612bba565b92505b5050919050565b600082821115612a7857612a7861296a60028585612bc6565b50900390565b600082612a8d575060006126f6565b82820282848281612a9a57fe5b041461093d5761093d61296a60018686612bc6565b60008282018381101561093d5761093d61296a60008686612bc6565b600081612ae157612ae161296a60038585612bc6565b6000828481612aec57fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b6060632800659560e01b848484604051602401612b2c93929190613424565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b600061093d8383612be5565b606063e946c1bb60e01b848484604051602401612b2c93929190613402565b60008160200183511015612c0657612c0661296a6005855185602001612b0d565b50016020015190565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b6040805160608101909152806000815260006020820181905260409091015290565b80356126f681613681565b600082601f830112612c74578081fd5b8135612c87612c8282613631565b61360a565b8181529150602080830190840160005b83811015612cc457612caf8760208435890101612d37565b83526020928301929190910190600101612c97565b5050505092915050565b600082601f830112612cde578081fd5b8135612cec612c8282613631565b818152915060208083019084810181840286018201871015612d0d57600080fd5b60005b84811015612d2c57813584529282019290820190600101612d10565b505050505092915050565b600082601f830112612d47578081fd5b813567ffffffffffffffff811115612d5d578182fd5b612d8e60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161360a565b9150808252836020828501011115612da557600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b81146126f657600080fd5b60006101c0808385031215612de3578182fd5b612dec8161360a565b915050612df98383612c59565b8152612e088360208401612c59565b6020820152612e1a8360408401612c59565b6040820152612e2c8360608401612c59565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff80821115612e8e57600080fd5b612e9a86838701612d37565b83850152610160925082850135915080821115612eb657600080fd5b612ec286838701612d37565b83850152610180925082850135915080821115612ede57600080fd5b612eea86838701612d37565b838501526101a0925082850135915080821115612f0657600080fd5b50612f1385828601612d37565b82840152505092915050565b600060208284031215612f30578081fd5b815161093d81613681565b600080600060608486031215612f4f578182fd5b8335612f5a81613681565b92506020840135612f6a81613681565b91506040840135612f7a81613681565b809150509250925092565b60008060008060808587031215612f9a578081fd5b8435612fa581613681565b93506020850135612fb581613681565b92506040850135612fc581613681565b9150606085013567ffffffffffffffff811115612fe0578182fd5b612fec87828801612cce565b91505092959194509250565b60008060006060848603121561300c578283fd5b833561301781613681565b9250602084013561302781613681565b9150604084013567ffffffffffffffff811115613042578182fd5b61304e86828701612cce565b9150509250925092565b6000806000806080858703121561306d578182fd5b843561307881613681565b93506130878660208701612dbe565b9250612fc58660408701612dbe565b600080602083850312156130a8578182fd5b823567ffffffffffffffff808211156130bf578384fd5b81850186601f8201126130d0578485fd5b80359250818311156130e0578485fd5b86602080850283010111156130f3578485fd5b60200196919550909350505050565b60008060408385031215613114578182fd5b823567ffffffffffffffff8082111561312b578384fd5b81850186601f82011261313c578485fd5b8035925061314c612c8284613631565b83815260208082019190838101885b87811015613184576131728c848435890101612dd0565b8552938201939082019060010161315b565b5091975088013594505050508082111561319c578283fd5b506131a985828601612c64565b9150509250929050565b600080600083850360a08112156131c8578182fd5b60608112156131d5578182fd5b506131e0606061360a565b8451600781106131ee578283fd5b815260208581015190820152604080860151908201526060850151608086015191945092508015158114612f7a578182fd5b600060208284031215613231578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845261326a816020860160208601613651565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b600082516132be818460208701613651565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b6000602080830181845280855180835260408601915060408482028701019250838701855b828110156133b2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526133a0858351613252565b94509285019290850190600101613366565b5092979650505050505050565b602080825282518282018190526000918401906040840190835b818110156133f75783518352602093840193909201916001016133d9565b509095945050505050565b606081016004851061341057fe5b938152602081019290925260409091015290565b606081016008851061341057fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b6000604082526134c1604083018551613238565b60208401516134d36060840182613238565b5060408401516134e66080840182613238565b5060608401516134f960a0840182613238565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c09150610180828187015261356a610200870185613252565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a08388830301818901526135a98287613252565b838b015196508489820301868a01526135c28188613252565b955050808a0151955050505080858303016101e0860152506135e48183613252565b84810360208601526135f68187613252565b979650505050505050565b90815260200190565b60405181810167ffffffffffffffff8111828210171561362957600080fd5b604052919050565b600067ffffffffffffffff821115613647578081fd5b5060209081020190565b60005b8381101561366c578181015183820152602001613654565b8381111561367b576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff811681146136a357600080fd5b5056fea365627a7a7231582001492aae0645e3694f9346a9de400473886ee87e4ce809789ba8b68683a5f2876c6578706572696d656e74616cf564736f6c63430005110040" + "object": "0x608060405234801561001057600080fd5b50600436106100ea5760003560e01c806368be3cf21161008c57806398cdafba1161006657806398cdafba146101d05780639f76ad35146101e3578063c7f7142e146101f6578063d0eea06d14610216576100ea565b806368be3cf21461018a5780636dd6b78d146101aa5780638b123a02146101bd576100ea565b80634cb8e253116100c85780634cb8e2531461013e57806359f515d01461015157806360ee052a1461016457806364ee6ade14610177576100ea565b80631796fb87146100ef578063354152a3146101185780634703a7e61461012b575b600080fd5b6101026100fd36600461299b565b610229565b60405161010f9190612e4a565b60405180910390f35b61010261012636600461299b565b6103fa565b6101026101393660046128c9565b6105b4565b61010261014c3660046128c9565b610790565b61010261015f366004612a45565b610a80565b6101026101723660046128c9565b610b29565b6101026101853660046128c9565b610de9565b61019d6101983660046129d9565b610faf565b60405161010f9190612dcc565b6101026101b83660046128c9565b6110ee565b6101026101cb366004612a45565b611396565b6101026101de366004612844565b61166f565b6101026101f13660046127d1565b6116ab565b610209610204366004612787565b6118b0565b60405161010f9190612cd9565b610102610224366004612929565b6119eb565b60606000825190508060405190808252806020026020018201604052801561025b578160200160208202803883390190505b50915060005b818110156103f057600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff16630e71d1b9905060e01b8a8a8a88815181106102b857fe5b60200260200101516040516024016102d293929190612e8d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161035b9190612cbd565b6000604051808303818686fa925050503d8060008114610397576040519150601f19603f3d011682016040523d82523d6000602084013e61039c565b606091505b509092509050600082156103c557818060200190516103be9190810190612bf6565b90506103cd565b5050506103f0565b808685815181106103da57fe5b6020908102919091010152505050600101610261565b5050949350505050565b60606000825190508060405190808252806020026020018201604052801561042c578160200160208202803883390190505b50915060005b818110156103f057600060608873ffffffffffffffffffffffffffffffffffffffff16620927c0600073ffffffffffffffffffffffffffffffffffffffff166307211ef7905060e01b8a8a8a888151811061048957fe5b60200260200101516040516024016104a393929190612e8d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161052c9190612cbd565b6000604051808303818686fa925050503d8060008114610568576040519150601f19603f3d011682016040523d82523d6000602084013e61056d565b606091505b509092509050600082156103c5578180602001905161058f9190810190612bf6565b90508086858151811061059e57fe5b6020908102919091010152505050600101610432565b60606105c08385611a1c565b81516040805182815260208084028201019091528180156105eb578160200160208202803883390190505b50915060005b818110156107875760006060610605611a8f565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663ff1fd974905060e01b8a8a8a888151811061064f57fe5b602002602001015160405160240161066993929190612d9b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516106f29190612cbd565b6000604051808303818686fa925050503d806000811461072e576040519150601f19603f3d011682016040523d82523d6000602084013e610733565b606091505b5090925090506000821561075c57818060200190516107559190810190612bf6565b9050610764565b505050610787565b8086858151811061077157fe5b60209081029190910101525050506001016105f1565b50509392505050565b606061079c8385611a1c565b60006107a6611aa7565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16146107de57846107f4565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b90506000610800611aa7565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614610838578461084e565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b9050600061085b87611abf565b60ff169050600061086b87611abf565b60ff169050600086519050806040519080825280602002602001820160405280156108a0578160200160208202803883390190505b50955060005b81811015610a7357600060606108ba611ad0565b73ffffffffffffffffffffffffffffffffffffffff166216e360600073ffffffffffffffffffffffffffffffffffffffff1663809a9e55905060e01b8a8a8e888151811061090457fe5b602002602001015160405160240161091e93929190612d9b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931790925290516109a79190612cbd565b6000604051808303818686fa925050503d80600081146109e3576040519150601f19603f3d011682016040523d82523d6000602084013e6109e8565b606091505b50909250905060008215610a115781806020019051610a0a9190810190612bf6565b9050610a19565b505050610a73565b670de0b6b3a764000087600a0a87600a0a8d8781518110610a3657fe5b602002602001015184020281610a4857fe5b0481610a5057fe5b048a8581518110610a5d57fe5b60209081029190910101525050506001016108a6565b5050505050509392505050565b6060610a8c8383611396565b905060005b8351811015610b2257818181518110610aa657fe5b6020026020010151600014610b1a57610b01828281518110610ac457fe5b6020026020010151858381518110610ad857fe5b602002602001015160a00151868481518110610af057fe5b602002602001015160800151611ae8565b828281518110610b0d57fe5b6020026020010181815250505b600101610a91565b5092915050565b6060610b358385611a1c565b8151604080518281526020808402820101909152818015610b60578160200160208202803883390190505b5091506000610b6d611aa7565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610bad57610ba886611b2a565b610bb0565b60005b90506000610bbc611aa7565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff1614610bfc57610bf786611b2a565b610bff565b60005b905060005b83811015610dde576001610c16611aa7565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415610cad578651610c8c9085907f2640f62c00000000000000000000000000000000000000000000000000000000908a9086908110610c7f57fe5b6020026020010151611bbc565b878481518110610c9857fe5b60200260200101819350828152505050610dca565b610cb5611aa7565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415610d1e578651610c8c9084907f59e9486200000000000000000000000000000000000000000000000000000000908a9086908110610c7f57fe5b8651600090610d579085907f59e9486200000000000000000000000000000000000000000000000000000000908b9087908110610c7f57fe5b925090508015610dad57610d8c857f2640f62c0000000000000000000000000000000000000000000000000000000083611bbc565b888581518110610d9857fe5b60200260200101819450828152505050610dc8565b6000878481518110610dbb57fe5b6020026020010181815250505b505b80610dd55750610dde565b50600101610c04565b505050509392505050565b6060610df58385611a1c565b8151604080518281526020808402820101909152818015610e20578160200160208202803883390190505b50915060005b818110156107875760006060610e3a611a8f565b73ffffffffffffffffffffffffffffffffffffffff16620f4240600073ffffffffffffffffffffffffffffffffffffffff1663144a2752905060e01b898b8a8881518110610e8457fe5b6020026020010151604051602401610e9e93929190612d9b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610f279190612cbd565b6000604051808303818686fa925050503d8060008114610f63576040519150601f19603f3d011682016040523d82523d6000602084013e610f68565b606091505b5090925090506000821561075c5781806020019051610f8a9190810190612bf6565b905080868581518110610f9957fe5b6020908102919091010152505050600101610e26565b604080518281526020808402820101909152606090828015610fe557816020015b6060815260200190600190039081610fd05790505b50905060005b808314610b2257600060603086868581811061100357fe5b6020028201905080357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13684900301811261103d57600080fd5b9091016020810191503567ffffffffffffffff81111561105c57600080fd5b3681900382131561106c57600080fd5b60405161107a929190612cad565b600060405180830381855afa9150503d80600081146110b5576040519150601f19603f3d011682016040523d82523d6000602084013e6110ba565b606091505b5091509150816110cc57805160208201fd5b808484815181106110d957fe5b60209081029190910101525050600101610feb565b60606110fa8385611a1c565b8151604080518281526020808402820101909152818015611125578160200160208202803883390190505b5091506000611132611aa7565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146111725761116d86611b2a565b611175565b60005b90506000611181611aa7565b73ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16146111c1576111bc86611b2a565b6111c4565b60005b905060005b83811015610dde5760016111db611aa7565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614156112655786516112449085907f95b68fe700000000000000000000000000000000000000000000000000000000908a9086908110610c7f57fe5b87848151811061125057fe5b60200260200101819350828152505050611382565b61126d611aa7565b73ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff1614156112d65786516112449084907fcd7724c300000000000000000000000000000000000000000000000000000000908a9086908110610c7f57fe5b865160009061130f9086907f95b68fe700000000000000000000000000000000000000000000000000000000908b9087908110610c7f57fe5b92509050801561136557611344847fcd7724c30000000000000000000000000000000000000000000000000000000083611bbc565b88858151811061135057fe5b60200260200101819450828152505050611380565b600087848151811061137357fe5b6020026020010181815250505b505b8061138d5750610dde565b506001016111c9565b606082516040519080825280602002602001820160405280156113c3578160200160208202803883390190505b506000805491925073ffffffffffffffffffffffffffffffffffffffff909116905b84518114611667578381815181106113f957fe5b60200260200101515160001480611427575084818151811061141757fe5b6020026020010151608001516000145b80611449575084818151811061143957fe5b602002602001015160a001516000145b1561146d57600083828151811061145c57fe5b60200260200101818152505061165f565b600060608373ffffffffffffffffffffffffffffffffffffffff166207a1208573ffffffffffffffffffffffffffffffffffffffff1663e77286eb905060e01b8986815181106114b957fe5b60200260200101518987815181106114cd57fe5b60200260200101516040516024016114e6929190612f08565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161156f9190612cbd565b6000604051808303818686fa925050503d80600081146115ab576040519150601f19603f3d011682016040523d82523d6000602084013e6115b0565b606091505b5091509150816115db5760008584815181106115c857fe5b602002602001018181525050505061165f565b6115e3612452565b600080838060200190516115fa9190810190612b89565b9194509250905060038351600681111561161057fe5b14158061161b575080155b1561163f57600088878151811061162e57fe5b602002602001018181525050611659565b8188878151811061164c57fe5b6020026020010181815250505b50505050505b6001016113e5565b505092915050565b606061169f858585857f9f76ad35000000000000000000000000000000000000000000000000000000008b611d05565b90505b95945050505050565b6060600082519050806040519080825280602002602001820160405280156116dd578160200160208202803883390190505b50915060006116ed8787876118b0565b905073ffffffffffffffffffffffffffffffffffffffff811661171257506118a89050565b60005b828110156118a457600060608373ffffffffffffffffffffffffffffffffffffffff1662030d40600073ffffffffffffffffffffffffffffffffffffffff1663343fbcdd905060e01b8b8b8b888151811061176c57fe5b602002602001015160405160240161178693929190612d9b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161180f9190612cbd565b6000604051808303818686fa925050503d806000811461184b576040519150601f19603f3d011682016040523d82523d6000602084013e611850565b606091505b5090925090506000821561187957818060200190516118729190810190612bf6565b9050611881565b5050506118a4565b8087858151811061188e57fe5b6020908102919091010152505050600101611715565b5050505b949350505050565b6040516000906060907f153f599700000000000000000000000000000000000000000000000000000000906118eb9086908690602401612cfa565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050509050600060608673ffffffffffffffffffffffffffffffffffffffff16836040516119739190612cbd565b600060405180830381855afa9150503d80600081146119ae576040519150601f19603f3d011682016040523d82523d6000602084013e6119b3565b606091505b50915091508180156119c6575080516020145b156119e0576119d681600c611ed7565b93505050506119e4565b5050505b9392505050565b60606116a2858585857f4cb8e253000000000000000000000000000000000000000000000000000000006000611d05565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611a8b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a8290612eab565b60405180910390fd5b5050565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b6000611aca82611f1c565b92915050565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b60006118a883611b1e611b0282600163ffffffff611fed16565b611b12888763ffffffff61200c16565b9063ffffffff61203d16565b9063ffffffff61205916565b6000611b34612083565b73ffffffffffffffffffffffffffffffffffffffff166306f2bf62836040518263ffffffff1660e01b8152600401611b6c9190612cd9565b60206040518083038186803b158015611b8457600080fd5b505afa158015611b98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611aca919081019061276b565b60008073ffffffffffffffffffffffffffffffffffffffff8516611bdf57611cfd565b60608573ffffffffffffffffffffffffffffffffffffffff16620249f08686604051602401611c0e919061305c565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611c979190612cbd565b6000604051808303818686fa925050503d8060008114611cd3576040519150601f19603f3d011682016040523d82523d6000602084013e611cd8565b606091505b5090925090508115611cfb5780806020019051611cf89190810190612bf6565b92505b505b935093915050565b6060611d118688611a1c565b8451611d1c57611ecd565b60008060008751604051908082528060200260200182016040528015611d4c578160200160208202803883390190505b509350611d70898b8a600081518110611d6157fe5b6020026020010151898961209b565b925082611d805750611ecd915050565b611d8d8a8a85898961209b565b915081611d9d5750611ecd915050565b60005b8851811015611ec8578751600101915060005b611dd18a8381518110611dc257fe5b60200260200101518587611ae8565b9450611de889600001516127100161271087611ae8565b9450611df78c8c878b8b61209b565b935083611e0357611e99565b898281518110611e0f57fe5b60200260200101518410611e5d576127108a8381518110611e2c57fe5b602002602001015181611e3b57fe5b048a8381518110611e4857fe5b6020026020010151850381611e5957fe5b0492505b898281518110611e6957fe5b602002602001015184108015611e7f5750885183115b8015611e945750886020015181600101915081105b611db3575b611ea88a8381518110611dc257fe5b868381518110611eb457fe5b602090810291909101015250600101611da0565b505050505b9695505050505050565b60008160140183511015611efd57611efd611ef86004855185601401612350565b6123f5565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b600060129050600060608373ffffffffffffffffffffffffffffffffffffffff166040518060400160405280600481526020017f313ce56700000000000000000000000000000000000000000000000000000000815250604051611f809190612cbd565b600060405180830381855afa9150503d8060008114611fbb576040519150601f19603f3d011682016040523d82523d6000602084013e611fc0565b606091505b5091509150818015611fd3575080516020145b15611fe657611fe38160006123fd565b92505b5050919050565b60008282111561200657612006611ef860028585612409565b50900390565b60008261201b57506000611aca565b8282028284828161202857fe5b04146119e4576119e4611ef860018686612409565b6000828201838110156119e4576119e4611ef860008686612409565b60008161206f5761206f611ef860038585612409565b600082848161207a57fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b60408051600180825281830190925260009160609182916020808301908038833901905050905085816000815181106120d057fe5b60209081029190910101527fffffffff0000000000000000000000000000000000000000000000000000000085167f4cb8e2530000000000000000000000000000000000000000000000000000000014156121e0576040517f4cb8e253000000000000000000000000000000000000000000000000000000009061215c908a908a908590602401612d62565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529150612299565b6040517f9f76ad3500000000000000000000000000000000000000000000000000000000906122199086908b908b908690602401612d21565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915291505b600060603073ffffffffffffffffffffffffffffffffffffffff16846040516122c29190612cbd565b600060405180830381855afa9150503d80600081146122fd576040519150601f19603f3d011682016040523d82523d6000602084013e612302565b606091505b5091509150816123195760009450505050506116a2565b8080602001905161232d9190810190612af6565b60008151811061233957fe5b602002602001015194505050505095945050505050565b6060632800659560e01b84848460405160240161236f93929190612e7f565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b805160208201fd5b60006119e48383612428565b606063e946c1bb60e01b84848460405160240161236f93929190612e5d565b6000816020018351101561244957612449611ef86005855185602001612350565b50016020015190565b6040805160608101909152806000815260006020820181905260409091015290565b8035611aca816130dc565b600082601f83011261248f578081fd5b81356124a261249d8261308c565b613065565b8181529150602080830190840160005b838110156124df576124ca8760208435890101612552565b835260209283019291909101906001016124b2565b5050505092915050565b600082601f8301126124f9578081fd5b813561250761249d8261308c565b81815291506020808301908481018184028601820187101561252857600080fd5b60005b848110156125475781358452928201929082019060010161252b565b505050505092915050565b600082601f830112612562578081fd5b813567ffffffffffffffff811115612578578182fd5b6125a960207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613065565b91508082528360208285010111156125c057600080fd5b8060208401602084013760009082016020015292915050565b8035600f81900b8114611aca57600080fd5b6000604082840312156125fc578081fd5b6126066040613065565b9050813581526020820135602082015292915050565b60006101c080838503121561262f578182fd5b61263881613065565b9150506126458383612474565b81526126548360208401612474565b60208201526126668360408401612474565b60408201526126788360608401612474565b60608201526080820135608082015260a082013560a082015260c082013560c082015260e082013560e08201526101008083013581830152506101208083013581830152506101408083013567ffffffffffffffff808211156126da57600080fd5b6126e686838701612552565b8385015261016092508285013591508082111561270257600080fd5b61270e86838701612552565b8385015261018092508285013591508082111561272a57600080fd5b61273686838701612552565b838501526101a092508285013591508082111561275257600080fd5b5061275f85828601612552565b82840152505092915050565b60006020828403121561277c578081fd5b81516119e4816130dc565b60008060006060848603121561279b578182fd5b83356127a6816130dc565b925060208401356127b6816130dc565b915060408401356127c6816130dc565b809150509250925092565b600080600080608085870312156127e6578081fd5b84356127f1816130dc565b93506020850135612801816130dc565b92506040850135612811816130dc565b9150606085013567ffffffffffffffff81111561282c578182fd5b612838878288016124e9565b91505092959194509250565b600080600080600060c0868803121561285b578283fd5b8535612866816130dc565b94506020860135612876816130dc565b93506040860135612886816130dc565b9250606086013567ffffffffffffffff8111156128a1578182fd5b6128ad888289016124e9565b9250506128bd87608088016125eb565b90509295509295909350565b6000806000606084860312156128dd578081fd5b83356128e8816130dc565b925060208401356128f8816130dc565b9150604084013567ffffffffffffffff811115612913578182fd5b61291f868287016124e9565b9150509250925092565b60008060008060a0858703121561293e578182fd5b8435612949816130dc565b93506020850135612959816130dc565b9250604085013567ffffffffffffffff811115612974578283fd5b612980878288016124e9565b92505061299086606087016125eb565b905092959194509250565b600080600080608085870312156129b0578182fd5b84356129bb816130dc565b93506129ca86602087016125d9565b925061281186604087016125d9565b600080602083850312156129eb578182fd5b823567ffffffffffffffff80821115612a02578384fd5b81850186601f820112612a13578485fd5b8035925081831115612a23578485fd5b8660208085028301011115612a36578485fd5b60200196919550909350505050565b60008060408385031215612a57578182fd5b823567ffffffffffffffff80821115612a6e578384fd5b81850186601f820112612a7f578485fd5b80359250612a8f61249d8461308c565b83815260208082019190838101885b87811015612ac757612ab58c84843589010161261c565b85529382019390820190600101612a9e565b50919750880135945050505080821115612adf578283fd5b50612aec8582860161247f565b9150509250929050565b60006020808385031215612b08578182fd5b825167ffffffffffffffff811115612b1e578283fd5b80840185601f820112612b2f578384fd5b80519150612b3f61249d8361308c565b8281528381019082850185850284018601891015612b5b578687fd5b8693505b84841015612b7d578051835260019390930192918501918501612b5f565b50979650505050505050565b600080600083850360a0811215612b9e578182fd5b6060811215612bab578182fd5b50612bb66060613065565b845160078110612bc4578283fd5b8152602085810151908201526040808601519082015260608501516080860151919450925080151581146127c6578182fd5b600060208284031215612c07578081fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b6000815180845260208401935060208301825b82811015612c59578151865260209586019590910190600101612c3b565b5093949350505050565b60008151808452612c7b8160208601602086016130ac565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b60008251612ccf8184602087016130ac565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401528085166040840152506080606083015261169f6080830184612c28565b600073ffffffffffffffffffffffffffffffffffffffff8086168352808516602084015250606060408301526116a26060830184612c28565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b6000602080830181845280855180835260408601915060408482028701019250838701855b82811015612e3d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452612e2b858351612c63565b94509285019290850190600101612df1565b5092979650505050505050565b6000602082526119e46020830184612c28565b6060810160048510612e6b57fe5b938152602081019290925260409091015290565b6060810160088510612e6b57fe5b600f93840b81529190920b6020820152604081019190915260600190565b60208082526025908201527f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e60408201527f5f50414952000000000000000000000000000000000000000000000000000000606082015260800190565b600060408252612f1c604083018551612c0e565b6020840151612f2e6060840182612c0e565b506040840151612f416080840182612c0e565b506060840151612f5460a0840182612c0e565b50608084015160c083015260a084015160e083015260c0840151610100818185015260e086015191506101208281860152818701519250610140915082828601528087015192505061016082818601528187015192506101c091506101808281870152612fc5610200870185612c63565b8289015194507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc092506101a08388830301818901526130048287612c63565b838b015196508489820301868a015261301d8188612c63565b955050808a0151955050505080858303016101e08601525061303f8183612c63565b84810360208601526130518187612c63565b979650505050505050565b90815260200190565b60405181810167ffffffffffffffff8111828210171561308457600080fd5b604052919050565b600067ffffffffffffffff8211156130a2578081fd5b5060209081020190565b60005b838110156130c75781810151838201526020016130af565b838111156130d6576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff811681146130fe57600080fd5b5056fea365627a7a723158208777a7f5c90c6021748d69e56d7d4136320b8e8d099fdd1cd108e182d1ee4d7f6c6578706572696d656e74616cf564736f6c63430005110040" } } }, diff --git a/packages/contract-artifacts/artifacts/IERC20BridgeSampler.json b/packages/contract-artifacts/artifacts/IERC20BridgeSampler.json index e62c683ade..965a7fd9fd 100644 --- a/packages/contract-artifacts/artifacts/IERC20BridgeSampler.json +++ b/packages/contract-artifacts/artifacts/IERC20BridgeSampler.json @@ -125,7 +125,16 @@ "inputs": [ { "internalType": "address", "name": "takerToken", "type": "address" }, { "internalType": "address", "name": "makerToken", "type": "address" }, - { "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" } + { "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" }, + { + "components": [ + { "internalType": "uint256", "name": "targetSlippageBps", "type": "uint256" }, + { "internalType": "uint256", "name": "maxIterations", "type": "uint256" } + ], + "internalType": "struct IERC20BridgeSampler.FakeBuyOptions", + "name": "opts", + "type": "tuple" + } ], "name": "sampleBuysFromKyberNetwork", "outputs": [{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }], @@ -139,7 +148,16 @@ { "internalType": "address", "name": "registryAddress", "type": "address" }, { "internalType": "address", "name": "takerToken", "type": "address" }, { "internalType": "address", "name": "makerToken", "type": "address" }, - { "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" } + { "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" }, + { + "components": [ + { "internalType": "uint256", "name": "targetSlippageBps", "type": "uint256" }, + { "internalType": "uint256", "name": "maxIterations", "type": "uint256" } + ], + "internalType": "struct IERC20BridgeSampler.FakeBuyOptions", + "name": "opts", + "type": "tuple" + } ], "name": "sampleBuysFromLiquidityProviderRegistry", "outputs": [{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }], @@ -278,20 +296,22 @@ }, "return": "takerTokenAmounts Taker amounts sold at each maker token amount." }, - "sampleBuysFromKyberNetwork(address,address,uint256[])": { + "sampleBuysFromKyberNetwork(address,address,uint256[],(uint256,uint256))": { "details": "Sample buy quotes from Kyber.", "params": { "makerToken": "Address of the maker token (what to buy).", "makerTokenAmounts": "Maker token buy amount for each sample.", + "opts": "`FakeBuyOptions` specifying target slippage and max iterations.", "takerToken": "Address of the taker token (what to sell)." }, "return": "takerTokenAmounts Taker amounts sold at each maker token amount." }, - "sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[])": { + "sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[],(uint256,uint256))": { "details": "Sample buy quotes from an arbitrary on-chain liquidity provider.", "params": { "makerToken": "Address of the maker token (what to buy).", "makerTokenAmounts": "Maker token buy amount for each sample.", + "opts": "`FakeBuyOptions` specifying target slippage and max iterations.", "registryAddress": "Address of the liquidity provider registry contract.", "takerToken": "Address of the taker token (what to sell)." }, diff --git a/packages/contract-wrappers/src/generated-wrappers/erc20_bridge_sampler.ts b/packages/contract-wrappers/src/generated-wrappers/erc20_bridge_sampler.ts index 458ec915f1..118a979fde 100644 --- a/packages/contract-wrappers/src/generated-wrappers/erc20_bridge_sampler.ts +++ b/packages/contract-wrappers/src/generated-wrappers/erc20_bridge_sampler.ts @@ -463,6 +463,20 @@ export class ERC20BridgeSamplerContract extends BaseContract { name: 'makerTokenAmounts', type: 'uint256[]', }, + { + name: 'opts', + type: 'tuple', + components: [ + { + name: 'targetSlippageBps', + type: 'uint256', + }, + { + name: 'maxIterations', + type: 'uint256', + }, + ], + }, ], name: 'sampleBuysFromKyberNetwork', outputs: [ @@ -494,6 +508,20 @@ export class ERC20BridgeSamplerContract extends BaseContract { name: 'makerTokenAmounts', type: 'uint256[]', }, + { + name: 'opts', + type: 'tuple', + components: [ + { + name: 'targetSlippageBps', + type: 'uint256', + }, + { + name: 'maxIterations', + type: 'uint256', + }, + ], + }, ], name: 'sampleBuysFromLiquidityProviderRegistry', outputs: [ @@ -1001,18 +1029,21 @@ export class ERC20BridgeSamplerContract extends BaseContract { * @param takerToken Address of the taker token (what to sell). * @param makerToken Address of the maker token (what to buy). * @param makerTokenAmounts Maker token buy amount for each sample. + * @param opts `FakeBuyOptions` specifying target slippage and max iterations. * @returns takerTokenAmounts Taker amounts sold at each maker token amount. */ public sampleBuysFromKyberNetwork( takerToken: string, makerToken: string, makerTokenAmounts: BigNumber[], + opts: { targetSlippageBps: BigNumber; maxIterations: BigNumber }, ): ContractFunctionObj { const self = (this as any) as ERC20BridgeSamplerContract; assert.isString('takerToken', takerToken); assert.isString('makerToken', makerToken); assert.isArray('makerTokenAmounts', makerTokenAmounts); - const functionSignature = 'sampleBuysFromKyberNetwork(address,address,uint256[])'; + + const functionSignature = 'sampleBuysFromKyberNetwork(address,address,uint256[],(uint256,uint256))'; return { async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { @@ -1030,6 +1061,7 @@ export class ERC20BridgeSamplerContract extends BaseContract { takerToken.toLowerCase(), makerToken.toLowerCase(), makerTokenAmounts, + opts, ]); }, }; @@ -1040,6 +1072,7 @@ export class ERC20BridgeSamplerContract extends BaseContract { * @param takerToken Address of the taker token (what to sell). * @param makerToken Address of the maker token (what to buy). * @param makerTokenAmounts Maker token buy amount for each sample. + * @param opts `FakeBuyOptions` specifying target slippage and max iterations. * @returns takerTokenAmounts Taker amounts sold at each maker token amount. */ public sampleBuysFromLiquidityProviderRegistry( @@ -1047,13 +1080,16 @@ export class ERC20BridgeSamplerContract extends BaseContract { takerToken: string, makerToken: string, makerTokenAmounts: BigNumber[], + opts: { targetSlippageBps: BigNumber; maxIterations: BigNumber }, ): ContractFunctionObj { const self = (this as any) as ERC20BridgeSamplerContract; assert.isString('registryAddress', registryAddress); assert.isString('takerToken', takerToken); assert.isString('makerToken', makerToken); assert.isArray('makerTokenAmounts', makerTokenAmounts); - const functionSignature = 'sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[])'; + + const functionSignature = + 'sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[],(uint256,uint256))'; return { async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { @@ -1072,6 +1108,7 @@ export class ERC20BridgeSamplerContract extends BaseContract { takerToken.toLowerCase(), makerToken.toLowerCase(), makerTokenAmounts, + opts, ]); }, }; diff --git a/packages/contract-wrappers/src/generated-wrappers/i_erc20_bridge_sampler.ts b/packages/contract-wrappers/src/generated-wrappers/i_erc20_bridge_sampler.ts index c16b69d439..ece77afc06 100644 --- a/packages/contract-wrappers/src/generated-wrappers/i_erc20_bridge_sampler.ts +++ b/packages/contract-wrappers/src/generated-wrappers/i_erc20_bridge_sampler.ts @@ -442,6 +442,20 @@ export class IERC20BridgeSamplerContract extends BaseContract { name: 'makerTokenAmounts', type: 'uint256[]', }, + { + name: 'opts', + type: 'tuple', + components: [ + { + name: 'targetSlippageBps', + type: 'uint256', + }, + { + name: 'maxIterations', + type: 'uint256', + }, + ], + }, ], name: 'sampleBuysFromKyberNetwork', outputs: [ @@ -473,6 +487,20 @@ export class IERC20BridgeSamplerContract extends BaseContract { name: 'makerTokenAmounts', type: 'uint256[]', }, + { + name: 'opts', + type: 'tuple', + components: [ + { + name: 'targetSlippageBps', + type: 'uint256', + }, + { + name: 'maxIterations', + type: 'uint256', + }, + ], + }, ], name: 'sampleBuysFromLiquidityProviderRegistry', outputs: [ @@ -978,18 +1006,21 @@ export class IERC20BridgeSamplerContract extends BaseContract { * @param takerToken Address of the taker token (what to sell). * @param makerToken Address of the maker token (what to buy). * @param makerTokenAmounts Maker token buy amount for each sample. + * @param opts `FakeBuyOptions` specifying target slippage and max iterations. * @returns takerTokenAmounts Taker amounts sold at each maker token amount. */ public sampleBuysFromKyberNetwork( takerToken: string, makerToken: string, makerTokenAmounts: BigNumber[], + opts: { targetSlippageBps: BigNumber; maxIterations: BigNumber }, ): ContractFunctionObj { const self = (this as any) as IERC20BridgeSamplerContract; assert.isString('takerToken', takerToken); assert.isString('makerToken', makerToken); assert.isArray('makerTokenAmounts', makerTokenAmounts); - const functionSignature = 'sampleBuysFromKyberNetwork(address,address,uint256[])'; + + const functionSignature = 'sampleBuysFromKyberNetwork(address,address,uint256[],(uint256,uint256))'; return { async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { @@ -1007,6 +1038,7 @@ export class IERC20BridgeSamplerContract extends BaseContract { takerToken.toLowerCase(), makerToken.toLowerCase(), makerTokenAmounts, + opts, ]); }, }; @@ -1017,6 +1049,7 @@ export class IERC20BridgeSamplerContract extends BaseContract { * @param takerToken Address of the taker token (what to sell). * @param makerToken Address of the maker token (what to buy). * @param makerTokenAmounts Maker token buy amount for each sample. + * @param opts `FakeBuyOptions` specifying target slippage and max iterations. * @returns takerTokenAmounts Taker amounts sold at each maker token amount. */ public sampleBuysFromLiquidityProviderRegistry( @@ -1024,13 +1057,16 @@ export class IERC20BridgeSamplerContract extends BaseContract { takerToken: string, makerToken: string, makerTokenAmounts: BigNumber[], + opts: { targetSlippageBps: BigNumber; maxIterations: BigNumber }, ): ContractFunctionObj { const self = (this as any) as IERC20BridgeSamplerContract; assert.isString('registryAddress', registryAddress); assert.isString('takerToken', takerToken); assert.isString('makerToken', makerToken); assert.isArray('makerTokenAmounts', makerTokenAmounts); - const functionSignature = 'sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[])'; + + const functionSignature = + 'sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[],(uint256,uint256))'; return { async callAsync(callData: Partial = {}, defaultBlock?: BlockParam): Promise { @@ -1049,6 +1085,7 @@ export class IERC20BridgeSamplerContract extends BaseContract { takerToken.toLowerCase(), makerToken.toLowerCase(), makerTokenAmounts, + opts, ]); }, }; From f2ee267f2baebefbed7d40791ed3681381cd8d11 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Tue, 21 Apr 2020 07:53:57 +1000 Subject: [PATCH 09/11] Feedback fixes --- .../contracts/src/ERC20BridgeSampler.sol | 27 +++--- .../test/erc20-bridge-sampler.ts | 6 +- .../utils/market_operation_utils/orders.ts | 8 +- .../sampler_operations.ts | 83 +++++++++---------- .../src/utils/quote_simulation.ts | 3 - .../asset-swapper/src/utils/source_utils.ts | 17 ++++ .../test/market_operation_utils_test.ts | 8 +- .../test/quote_simulation_test.ts | 38 ++++----- 8 files changed, 94 insertions(+), 96 deletions(-) create mode 100644 packages/asset-swapper/src/utils/source_utils.ts diff --git a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol index a30e045e15..e8a376e18a 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol @@ -684,17 +684,17 @@ contract ERC20BridgeSampler is function _sampleSellForApproximateBuy( address takerToken, address makerToken, - uint256 makerTokenAmount, + uint256 takerTokenAmount, bytes4 selector, address plpRegistryAddress ) private view - returns (uint256 takerTokenAmount) + returns (uint256 makerTokenAmount) { bytes memory callData; uint256[] memory tmpTakerAmounts = new uint256[](1); - tmpTakerAmounts[0] = makerTokenAmount; + tmpTakerAmounts[0] = takerTokenAmount; if (selector == this.sampleSellsFromKyberNetwork.selector) { callData = abi.encodeWithSelector( this.sampleSellsFromKyberNetwork.selector, @@ -716,7 +716,7 @@ contract ERC20BridgeSampler is return 0; } // solhint-disable indent - takerTokenAmount = abi.decode(resultData, (uint256[]))[0]; + makerTokenAmount = abi.decode(resultData, (uint256[]))[0]; } function _sampleApproximateBuysFromSource( @@ -763,17 +763,13 @@ contract ERC20BridgeSampler is } for (uint256 i = 0; i < makerTokenAmounts.length; i++) { - uint256 iteration = 0; - // Default to some value higher than our target - slippageFromTarget = opts.targetSlippageBps + 1; - do { + for (uint256 iter = 0; iter < opts.maxIterations; iter++) { // adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER sellAmount = LibMath.getPartialAmountCeil( makerTokenAmounts[i], buyAmount, sellAmount ); - // JUMP Multiplier by 1.0005 sellAmount = LibMath.getPartialAmountCeil( (10000 + opts.targetSlippageBps), 10000, @@ -790,15 +786,14 @@ contract ERC20BridgeSampler is break; } - // 0.0005 slippage is the target if (buyAmount >= makerTokenAmounts[i]) { - slippageFromTarget = (buyAmount - makerTokenAmounts[i]) / - (makerTokenAmounts[i] / 10000); + uint256 slippageFromTarget = (buyAmount - makerTokenAmounts[i]) * 10000 / + makerTokenAmounts[i]; + if (slippageFromTarget <= opts.targetSlippageBps) { + break; + } } - } while ( - (buyAmount < makerTokenAmounts[i] && slippageFromTarget > opts.targetSlippageBps) && - ++iteration < opts.maxIterations - ); + } // We do our best to close in on the requested amount, but we can either over buy or under buy and exit // if we hit a max iteration limit // We scale the sell amount to get the approximate target diff --git a/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts b/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts index ecb243179b..faacb5c6e3 100644 --- a/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts +++ b/contracts/erc20-bridge-sampler/test/erc20-bridge-sampler.ts @@ -341,7 +341,7 @@ blockchainTests('erc20-bridge-sampler', env => { expect(quotes).to.deep.eq([]); }); - it('can quote token - token', async () => { + it('can quote token -> token', async () => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts); const quotes = await testContract @@ -421,7 +421,7 @@ blockchainTests('erc20-bridge-sampler', env => { expectedQuotes: BigNumber[], maxSlippage: BigNumber | number, ) => { - quotes.map((_q, i) => { + quotes.forEach((_q, i) => { // If we're within 1 base unit of a low decimal token // then that's as good as we're going to get (and slippage is "high") if ( @@ -443,7 +443,7 @@ blockchainTests('erc20-bridge-sampler', env => { }); }; - it('can quote token - token', async () => { + it('can quote token -> token', async () => { const sampleAmounts = getSampleAmounts(TAKER_TOKEN); const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts); const quotes = await testContract diff --git a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts index 018055f5fc..6dc58025bb 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/orders.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/orders.ts @@ -4,9 +4,9 @@ import { ERC20BridgeAssetData, SignedOrder } from '@0x/types'; import { AbiEncoder, BigNumber } from '@0x/utils'; import { MarketOperation, SignedOrderWithFillableAmounts } from '../../types'; +import { getCurveInfo, isCurveSource } from '../source_utils'; import { - DEFAULT_CURVE_OPTS, ERC20_PROXY_ID, NULL_ADDRESS, NULL_BYTES, @@ -210,10 +210,8 @@ function createBridgeOrder(fill: CollapsedFill, opts: CreateOrderFromPathOpts): const bridgeAddress = getBridgeAddressFromSource(fill.source, opts); let makerAssetData; - if (Object.keys(DEFAULT_CURVE_OPTS).includes(fill.source)) { - const { curveAddress, tokens, version } = DEFAULT_CURVE_OPTS[fill.source]; - const fromTokenIdx = tokens.indexOf(takerToken); - const toTokenIdx = tokens.indexOf(makerToken); + if (isCurveSource(fill.source)) { + const { curveAddress, fromTokenIdx, toTokenIdx, version } = getCurveInfo(fill.source, takerToken, makerToken); makerAssetData = assetDataUtils.encodeERC20BridgeAssetData( makerToken, bridgeAddress, diff --git a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts index 17df10c200..e3ffb3d57c 100644 --- a/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts +++ b/packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts @@ -1,6 +1,7 @@ import { BigNumber, ERC20BridgeSource, SignedOrder } from '../..'; +import { getCurveInfo, isCurveSource } from '../source_utils'; -import { DEFAULT_CURVE_OPTS, DEFAULT_FAKE_BUY_OPTS } from './constants'; +import { DEFAULT_FAKE_BUY_OPTS } from './constants'; import { BatchedOperation, DexSample, FakeBuyOpts } from './types'; /** @@ -52,7 +53,7 @@ export const samplerOperations = { makerToken: string, takerToken: string, makerFillAmounts: BigNumber[], - fakeBuyOpts: FakeBuyOpts, + fakeBuyOpts: FakeBuyOpts = DEFAULT_FAKE_BUY_OPTS, ): BatchedOperation { return { encodeCall: contract => { @@ -81,6 +82,22 @@ export const samplerOperations = { }, }; }, + getUniswapBuyQuotes( + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ): BatchedOperation { + return { + encodeCall: contract => { + return contract + .sampleBuysFromUniswap(takerToken, makerToken, makerFillAmounts) + .getABIEncodedTransactionData(); + }, + handleCallResultsAsync: async (contract, callResults) => { + return contract.getABIDecodedReturnData('sampleBuysFromUniswap', callResults); + }, + }; + }, getLiquidityProviderSellQuotes( liquidityProviderRegistryAddress: string, makerToken: string, @@ -111,7 +128,7 @@ export const samplerOperations = { makerToken: string, takerToken: string, makerFillAmounts: BigNumber[], - fakeBuyOpts: FakeBuyOpts, + fakeBuyOpts: FakeBuyOpts = DEFAULT_FAKE_BUY_OPTS, ): BatchedOperation { return { encodeCall: contract => { @@ -149,6 +166,22 @@ export const samplerOperations = { }, }; }, + getEth2DaiBuyQuotes( + makerToken: string, + takerToken: string, + makerFillAmounts: BigNumber[], + ): BatchedOperation { + return { + encodeCall: contract => { + return contract + .sampleBuysFromEth2Dai(takerToken, makerToken, makerFillAmounts) + .getABIEncodedTransactionData(); + }, + handleCallResultsAsync: async (contract, callResults) => { + return contract.getABIDecodedReturnData('sampleBuysFromEth2Dai', callResults); + }, + }; + }, getCurveSellQuotes( curveAddress: string, fromTokenIdx: number, @@ -193,38 +226,6 @@ export const samplerOperations = { }, }; }, - getUniswapBuyQuotes( - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): BatchedOperation { - return { - encodeCall: contract => { - return contract - .sampleBuysFromUniswap(takerToken, makerToken, makerFillAmounts) - .getABIEncodedTransactionData(); - }, - handleCallResultsAsync: async (contract, callResults) => { - return contract.getABIDecodedReturnData('sampleBuysFromUniswap', callResults); - }, - }; - }, - getEth2DaiBuyQuotes( - makerToken: string, - takerToken: string, - makerFillAmounts: BigNumber[], - ): BatchedOperation { - return { - encodeCall: contract => { - return contract - .sampleBuysFromEth2Dai(takerToken, makerToken, makerFillAmounts) - .getABIEncodedTransactionData(); - }, - handleCallResultsAsync: async (contract, callResults) => { - return contract.getABIDecodedReturnData('sampleBuysFromEth2Dai', callResults); - }, - }; - }, getMedianSellRate( sources: ERC20BridgeSource[], makerToken: string, @@ -307,10 +308,8 @@ export const samplerOperations = { batchedOperation = samplerOperations.getUniswapSellQuotes(makerToken, takerToken, takerFillAmounts); } else if (source === ERC20BridgeSource.Kyber) { batchedOperation = samplerOperations.getKyberSellQuotes(makerToken, takerToken, takerFillAmounts); - } else if (Object.keys(DEFAULT_CURVE_OPTS).includes(source)) { - const { curveAddress, tokens } = DEFAULT_CURVE_OPTS[source]; - const fromTokenIdx = tokens.indexOf(takerToken); - const toTokenIdx = tokens.indexOf(makerToken); + } else if (isCurveSource(source)) { + const { curveAddress, fromTokenIdx, toTokenIdx } = getCurveInfo(source, takerToken, makerToken); if (fromTokenIdx !== -1 && toTokenIdx !== -1) { batchedOperation = samplerOperations.getCurveSellQuotes( curveAddress, @@ -384,10 +383,8 @@ export const samplerOperations = { makerFillAmounts, fakeBuyOpts, ); - } else if (Object.keys(DEFAULT_CURVE_OPTS).includes(source)) { - const { curveAddress, tokens } = DEFAULT_CURVE_OPTS[source]; - const fromTokenIdx = tokens.indexOf(takerToken); - const toTokenIdx = tokens.indexOf(makerToken); + } else if (isCurveSource(source)) { + const { curveAddress, fromTokenIdx, toTokenIdx } = getCurveInfo(source, takerToken, makerToken); if (fromTokenIdx !== -1 && toTokenIdx !== -1) { batchedOperation = samplerOperations.getCurveBuyQuotes( curveAddress, diff --git a/packages/asset-swapper/src/utils/quote_simulation.ts b/packages/asset-swapper/src/utils/quote_simulation.ts index 8310caf8b3..a12902fbd1 100644 --- a/packages/asset-swapper/src/utils/quote_simulation.ts +++ b/packages/asset-swapper/src/utils/quote_simulation.ts @@ -101,7 +101,6 @@ export function simulateBestCaseFill(quoteInfo: QuoteFillInfo): QuoteFillResult ...quoteInfo.opts, }; const result = fillQuoteOrders( - quoteInfo.side, createBestCaseFillOrderCalls(quoteInfo), quoteInfo.fillAmount, quoteInfo.gasPrice.times(opts.protocolFeeMultiplier), @@ -119,7 +118,6 @@ export function simulateWorstCaseFill(quoteInfo: QuoteFillInfo): QuoteFillResult const protocolFeePerFillOrder = quoteInfo.gasPrice.times(opts.protocolFeeMultiplier); const result = { ...fillQuoteOrders( - quoteInfo.side, createWorstCaseFillOrderCalls(quoteInfo), quoteInfo.fillAmount, protocolFeePerFillOrder, @@ -136,7 +134,6 @@ export function simulateWorstCaseFill(quoteInfo: QuoteFillInfo): QuoteFillResult } export function fillQuoteOrders( - _side: MarketOperation, fillOrders: QuoteFillOrderCall[], inputAmount: BigNumber, protocolFeePerFillOrder: BigNumber, diff --git a/packages/asset-swapper/src/utils/source_utils.ts b/packages/asset-swapper/src/utils/source_utils.ts new file mode 100644 index 0000000000..d4aa0412f5 --- /dev/null +++ b/packages/asset-swapper/src/utils/source_utils.ts @@ -0,0 +1,17 @@ +import { DEFAULT_CURVE_OPTS } from './market_operation_utils/constants'; +import { ERC20BridgeSource } from './market_operation_utils/types'; + +export const isCurveSource = (source: ERC20BridgeSource): boolean => { + return Object.keys(DEFAULT_CURVE_OPTS).includes(source); +}; + +export const getCurveInfo = ( + source: ERC20BridgeSource, + takerToken: string, + makerToken: string, +): { curveAddress: string; fromTokenIdx: number; toTokenIdx: number; version: number } => { + const { curveAddress, tokens, version } = DEFAULT_CURVE_OPTS[source]; + const fromTokenIdx = tokens.indexOf(takerToken); + const toTokenIdx = tokens.indexOf(makerToken); + return { curveAddress, fromTokenIdx, toTokenIdx, version }; +}; diff --git a/packages/asset-swapper/test/market_operation_utils_test.ts b/packages/asset-swapper/test/market_operation_utils_test.ts index e60743d855..1e90b00fff 100644 --- a/packages/asset-swapper/test/market_operation_utils_test.ts +++ b/packages/asset-swapper/test/market_operation_utils_test.ts @@ -526,7 +526,6 @@ describe('MarketOperationUtils tests', () => { [ERC20BridgeSource.Eth2Dai]: [0.92, 0.1, 0.1, 0.1], // Effectively [0.8, ~0.5, ~0, ~0] [ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2], - [ERC20BridgeSource.Kyber]: [0, 0, 0, 0], }; const feeSchedule = { [ERC20BridgeSource.Uniswap]: FILL_AMOUNT.div(4) @@ -722,7 +721,7 @@ describe('MarketOperationUtils tests', () => { sampleDistributionBase: 1, bridgeSlippage: 0, maxFallbackSlippage: 100, - excludedSources: Object.keys(DEFAULT_CURVE_OPTS) as ERC20BridgeSource[], + excludedSources: [...(Object.keys(DEFAULT_CURVE_OPTS) as ERC20BridgeSource[]), ERC20BridgeSource.Kyber], allowFallback: false, shouldBatchBridgeOrders: false, }; @@ -859,7 +858,6 @@ describe('MarketOperationUtils tests', () => { rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.2, 0.1]; rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05]; rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05]; - rates[ERC20BridgeSource.Kyber] = [0, 0, 0, 0]; // Unused replaceSamplerOps({ getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), }); @@ -923,7 +921,6 @@ describe('MarketOperationUtils tests', () => { // Effectively [0.8, ~0.5, ~0, ~0] [ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2], [ERC20BridgeSource.Eth2Dai]: [0.92, 0.1, 0.1, 0.1], - [ERC20BridgeSource.Kyber]: [0, 0, 0, 0], }; const feeSchedule = { [ERC20BridgeSource.Uniswap]: FILL_AMOUNT.div(4) @@ -953,7 +950,6 @@ describe('MarketOperationUtils tests', () => { rates[ERC20BridgeSource.Native] = [0.9, 0.8, 0.5, 0.5]; rates[ERC20BridgeSource.Uniswap] = [0.6, 0.05, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.4, 0.3, 0.01, 0.01]; - rates[ERC20BridgeSource.Kyber] = [0, 0, 0, 0]; replaceSamplerOps({ getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), }); @@ -979,7 +975,6 @@ describe('MarketOperationUtils tests', () => { rates[ERC20BridgeSource.Native] = [1, 1, 0.01, 0.01]; rates[ERC20BridgeSource.Uniswap] = [1, 1, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.49, 0.49, 0.49]; - rates[ERC20BridgeSource.Kyber] = [0, 0, 0, 0]; replaceSamplerOps({ getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), }); @@ -1000,7 +995,6 @@ describe('MarketOperationUtils tests', () => { rates[ERC20BridgeSource.Native] = [0.5, 0.01, 0.01, 0.01]; rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.01, 0.01, 0.01]; rates[ERC20BridgeSource.Uniswap] = [0.48, 0.47, 0.01, 0.01]; - rates[ERC20BridgeSource.Kyber] = [0, 0, 0, 0]; replaceSamplerOps({ getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates), }); diff --git a/packages/asset-swapper/test/quote_simulation_test.ts b/packages/asset-swapper/test/quote_simulation_test.ts index 45b3d5a74b..cb78ac2177 100644 --- a/packages/asset-swapper/test/quote_simulation_test.ts +++ b/packages/asset-swapper/test/quote_simulation_test.ts @@ -232,7 +232,7 @@ describe('quote_simulation tests', async () => { fillsCount, count: 1, }); - const result = fillQuoteOrders(side, fillOrders, fillableInput, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); expect(totalFilledInput).to.bignumber.eq(fillableInput); @@ -254,7 +254,7 @@ describe('quote_simulation tests', async () => { count: 1, }); const inputFillAmount = fillableInput.times(2 / 3).integerValue(); - const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); expect(totalFilledInput).to.bignumber.eq(inputFillAmount); @@ -280,7 +280,7 @@ describe('quote_simulation tests', async () => { count: 1, }); const inputFillAmount = fillableInput.times(2 / 3).integerValue(); - const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); expect(totalFilledInput).to.bignumber.eq(inputFillAmount); @@ -303,7 +303,7 @@ describe('quote_simulation tests', async () => { count: 1, }); const inputFillAmount = fillableInput.times(3 / 2).integerValue(); - const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); expect(totalFilledInput).to.bignumber.eq(fillableInput); @@ -328,7 +328,7 @@ describe('quote_simulation tests', async () => { }); const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate; const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue(); - const result = fillQuoteOrders(side, fillOrders, totalFillableInput, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, totalFillableInput, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); assertIntegerRoughlyEquals(totalFilledInput, totalFillableInput, EPS); @@ -355,7 +355,7 @@ describe('quote_simulation tests', async () => { const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate; const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue(); const inputFillAmount = totalFillableInput.times(2 / 3).integerValue(); - const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); assertIntegerRoughlyEquals(totalFilledInput, inputFillAmount, EPS); @@ -382,7 +382,7 @@ describe('quote_simulation tests', async () => { const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate; const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue(); const inputFillAmount = totalFillableInput.times(3 / 2).integerValue(); - const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); assertIntegerRoughlyEquals(totalFilledInput, totalFillableInput, EPS); @@ -408,7 +408,7 @@ describe('quote_simulation tests', async () => { }); const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate; const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue(); - const result = fillQuoteOrders(side, fillOrders, fillableInput, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); assertIntegerRoughlyEquals(totalFilledInput, fillableInput, EPS); @@ -435,7 +435,7 @@ describe('quote_simulation tests', async () => { const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate; const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue(); const inputFillAmount = fillableInput.times(2 / 3).integerValue(); - const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); assertIntegerRoughlyEquals(totalFilledInput, inputFillAmount, EPS); @@ -462,7 +462,7 @@ describe('quote_simulation tests', async () => { const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate; const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue(); const inputFillAmount = fillableInput.times(3 / 2).integerValue(); - const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); assertIntegerRoughlyEquals(totalFilledInput, fillableInput, EPS); @@ -479,7 +479,7 @@ describe('quote_simulation tests', async () => { const fillableInput = getRandomOrderSize(); const fillableOutput = getRandomOrderSize(); const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side }); - const result = fillQuoteOrders(side, fillOrders, fillableInput, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); expect(totalFilledInput).to.bignumber.eq(fillableInput); @@ -494,7 +494,7 @@ describe('quote_simulation tests', async () => { const fillableOutput = getRandomOrderSize(); const inputFillAmount = fillableInput.times(2 / 3).integerValue(); const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side }); - const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); expect(totalFilledInput).to.bignumber.eq(inputFillAmount); @@ -508,7 +508,7 @@ describe('quote_simulation tests', async () => { const fillableOutput = getRandomOrderSize(); const inputFillAmount = fillableInput.times(3 / 2).integerValue(); const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side }); - const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); expect(totalFilledInput).to.bignumber.eq(fillableInput); @@ -530,7 +530,7 @@ describe('quote_simulation tests', async () => { }); const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate; const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue(); - const result = fillQuoteOrders(side, fillOrders, totalFillableInput, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, totalFillableInput, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); assertIntegerRoughlyEquals(totalFilledInput, totalFillableInput, EPS); @@ -554,7 +554,7 @@ describe('quote_simulation tests', async () => { const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate; const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue(); const inputFillAmount = totalFillableInput.times(2 / 3).integerValue(); - const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); assertIntegerRoughlyEquals(totalFilledInput, inputFillAmount, EPS); @@ -578,7 +578,7 @@ describe('quote_simulation tests', async () => { const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate; const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue(); const inputFillAmount = totalFillableInput.times(3 / 2).integerValue(); - const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); assertIntegerRoughlyEquals(totalFilledInput, totalFillableInput, EPS); @@ -601,7 +601,7 @@ describe('quote_simulation tests', async () => { }); const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate; const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue(); - const result = fillQuoteOrders(side, fillOrders, fillableInput, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); assertIntegerRoughlyEquals(totalFilledInput, fillableInput, EPS); @@ -625,7 +625,7 @@ describe('quote_simulation tests', async () => { const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate; const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue(); const inputFillAmount = fillableInput.times(2 / 3).integerValue(); - const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); assertIntegerRoughlyEquals(totalFilledInput, inputFillAmount, EPS); @@ -649,7 +649,7 @@ describe('quote_simulation tests', async () => { const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate; const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue(); const inputFillAmount = fillableInput.times(3 / 2).integerValue(); - const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); + const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE); const totalFilledInput = result.input.plus(result.inputFee); const totalFilledOutput = result.output.plus(result.outputFee); assertIntegerRoughlyEquals(totalFilledInput, fillableInput, EPS); From b3343b1172fececa55733ca43f016c139e5bb8bb Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Tue, 21 Apr 2020 11:14:44 +1000 Subject: [PATCH 10/11] Handle OOG/revert 0s --- .../contracts/src/ERC20BridgeSampler.sol | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol index e8a376e18a..b11658576f 100644 --- a/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol +++ b/contracts/erc20-bridge-sampler/contracts/src/ERC20BridgeSampler.sol @@ -775,17 +775,20 @@ contract ERC20BridgeSampler is 10000, sellAmount ); - buyAmount = _sampleSellForApproximateBuy( + uint256 _buyAmount = _sampleSellForApproximateBuy( takerToken, makerToken, sellAmount, selector, plpRegistryAddress ); - if (buyAmount == 0) { + if (_buyAmount == 0) { break; } - + // We re-use buyAmount next iteration, only assign if it is + // non zero + buyAmount = _buyAmount; + // If we've reached our goal, exit early if (buyAmount >= makerTokenAmounts[i]) { uint256 slippageFromTarget = (buyAmount - makerTokenAmounts[i]) * 10000 / makerTokenAmounts[i]; From 34fd9d62947275e51894e23f68c5bd7f566cd8d6 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Tue, 21 Apr 2020 12:48:16 +1000 Subject: [PATCH 11/11] Redeploy Mainnet refactor --- packages/contract-addresses/addresses.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contract-addresses/addresses.json b/packages/contract-addresses/addresses.json index f159a55077..fa33fd2f9e 100644 --- a/packages/contract-addresses/addresses.json +++ b/packages/contract-addresses/addresses.json @@ -20,7 +20,7 @@ "devUtils": "0x74134cf88b21383713e096a5ecf59e297dc7f547", "erc20BridgeProxy": "0x8ed95d1746bf1e4dab58d8ed4724f1ef95b20db0", "uniswapBridge": "0x36691c4f426eb8f42f150ebde43069a31cb080ad", - "erc20BridgeSampler": "0x867d1b78e7b5c2ac060e7bdeec6d8419a5c6ca4b", + "erc20BridgeSampler": "0xabee9a41f928c3b3b799b239a0f524343c7260c5", "kyberBridge": "0x1c29670f7a77f1052d30813a0a4f632c78a02610", "eth2DaiBridge": "0x991c745401d5b5e469b8c3e2cb02c748f08754f1", "chaiBridge": "0x77c31eba23043b9a72d13470f3a3a311344d7438",