From 82682a264fee34e019122253bd01ddaec8c4fb5b Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 31 Mar 2020 00:05:00 +0300 Subject: [PATCH 01/60] Almost done with OneSplitSmartTokenView --- .gitignore | 1 + contracts/OneSplitSmartToken.sol | 181 +++++++++++-------- contracts/interface/ISmartTokenConverter.sol | 17 +- test/OneSplit.js | 26 ++- 4 files changed, 143 insertions(+), 82 deletions(-) diff --git a/.gitignore b/.gitignore index dd5f792..43f55ef 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules/ coverage/ coverage.json build/ +.vscode \ No newline at end of file diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index f614b5e..681f834 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -1,4 +1,5 @@ pragma solidity ^0.5.0; +pragma experimental ABIEncoderV2; import "./interface/ISmartToken.sol"; import "./interface/ISmartTokenRegistry.sol"; @@ -30,28 +31,48 @@ contract OneSplitSmartTokenBase { tokens.ratios = new uint256[](tokens.tokens.length); for (uint256 i = 0; i < tokens.tokens.length; i++) { tokens.tokens[i] = converter.connectorTokens(i); - tokens.ratios[i] = converter.getReserveRatio(tokens.tokens[i]); + tokens.ratios[i] = _getReserveRatio(converter, tokens.tokens[i]); tokens.totalRatio = tokens.totalRatio.add(tokens.ratios[i]); } + return tokens; + } + + function _getReserveRatio( + ISmartTokenConverter converter, + IERC20 token + ) + internal + view + returns (uint256) + { + uint16 version = converter.version(); + + if (version >= 22) { + return converter.getReserveRatio(token); + } + + return uint256(converter.reserves(address(token)).ratio); } } contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { + function getExpectedReturn( IERC20 fromToken, IERC20 toToken, - uint256 amount, uint256 parts, - uint256 disableFlags + uint256 disableFlags, + uint256 amount ) public view returns( - uint256 returnAmount, + uint256, uint256[] memory distribution ) { + if (fromToken == toToken) { return (amount, new uint256[](9)); } @@ -59,86 +80,89 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { if (!disableFlags.check(FLAG_DISABLE_SMART_TOKEN)) { distribution = new uint256[](9); if (smartTokenRegistry.isSmartToken(fromToken)) { - this; - // ISmartTokenConverter converter = ISmartToken(address(fromToken)).owner(); - - // TokensWithRatio memory tokens = _getTokens(converter); - - // for (uint256 i = 0; i < tokens.tokens.length; i++) { - // uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( - // toToken.totalSupply(), - // tokens.tokens[i].balanceOf(address(converter)), - // uint32(tokens.totalRatio), - // amount - // ); - - // (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( - // tokens.tokens[i], - // toToken, - // srcAmount, - // parts, - // disableFlags - // ); - - // returnAmount = returnAmount.add(ret); - // for (uint j = 0; j < distribution.length; j++) { - // distribution[j] = distribution[j].add(dist[j] << (i * 8)); - // } - // } - // return (returnAmount, distribution); + ISmartTokenConverter converter = ISmartToken(address(fromToken)).owner(); + + TokensWithRatio memory tokens = _getTokens(converter); + + uint256 returnAmount = 0; + for (uint256 i = 0; i < tokens.tokens.length; i++) { + uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( + toToken.totalSupply(), + tokens.tokens[i].balanceOf(address(converter)), + uint32(tokens.totalRatio), + amount + ); + + (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( + tokens.tokens[i], + toToken, + srcAmount, + parts, + disableFlags + ); + + returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] = distribution[j].add(dist[j] << (i * 8)); + } + } + return (returnAmount, distribution); } if (smartTokenRegistry.isSmartToken(toToken)) { - this; - // ISmartTokenConverter converter = ISmartToken(address(fromToken)).owner(); - - // TokensWithRatio memory tokens = _getTokens(converter); - - // uint256 minFundAmount = uint256(-1); - // uint256[] memory fundAmounts = new uint256[](tokens.tokens.length); - // for (uint256 i = 0; i < tokens.tokens.length; i++) { - // (uint256 tokenAmount, uint256[] memory dist) = super.getExpectedReturn( - // fromToken, - // tokens.tokens[i], - // amount.mul(tokens.ratios[i]).div(tokens.totalRatio), - // parts, - // disableFlags | FLAG_DISABLE_BANCOR - // ); - // for (uint j = 0; j < distribution.length; j++) { - // distribution[j] = distribution[j].add(dist[j] << (i * 8)); - // } - - // fundAmounts[i] = toToken.totalSupply() - // .mul(tokenAmount) - // .div(tokens.tokens[i].balanceOf(address(converter))); - - // if (fundAmounts[i] < minFundAmount) { - // minFundAmount = fundAmounts[i]; - // } - // } - - // // Swap leftovers for SmartToken - // for (uint256 i = 0; i < tokens.tokens.length; i++) { - // uint256 leftover = fundAmounts[i].sub(minFundAmount) - // .mul(tokens.tokens[i].balanceOf(address(converter))) - // .div(toToken.totalSupply()); - - // if (leftover > 0) { - // minFundAmount = minFundAmount.add( - // smartTokenFormula.calculatePurchaseReturn( - // toToken.totalSupply(), - // tokens.tokens[i].balanceOf(address(converter)), - // uint32(tokens.totalRatio), - // leftover - // ) - // ); - // } - // } - - // return (minFundAmount, distribution); + ISmartTokenConverter converter = ISmartToken(address(toToken)).owner(); + + TokensWithRatio memory tokens = _getTokens( + converter + ); + + uint256[] memory fundAmounts = new uint256[](tokens.tokens.length + 1); + fundAmounts[0] = uint256(-1); + for (uint256 i = 0; i < 1; i++) { + (uint256 tokenAmount, uint256[] memory dist) = super.getExpectedReturn( + fromToken, + tokens.tokens[i], + amount.mul(tokens.ratios[i]).div(tokens.totalRatio), + parts, + disableFlags | FLAG_DISABLE_BANCOR + ); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] = distribution[j].add(dist[j] << (i * 8)); + } + + fundAmounts[i + 1] = toToken.totalSupply() + .mul(tokenAmount) + .div(tokens.tokens[i].balanceOf(address(converter))); + + if (fundAmounts[i + 1] < fundAmounts[0]) { + fundAmounts[0] = fundAmounts[i + 1]; + } + } + + // Swap leftovers for SmartToken + for (uint i = 0; i < 1; i++) { + uint256 leftover = fundAmounts[i + 1].sub(fundAmounts[0]) + .mul(tokens.tokens[i].balanceOf(address(converter))) + .div(toToken.totalSupply()); + + if (leftover > 0) { + fundAmounts[0] = fundAmounts[0].add( + smartTokenFormula.calculatePurchaseReturn( + toToken.totalSupply(), + tokens.tokens[i].balanceOf(address(converter)), + uint32(tokens.totalRatio), + leftover + ) + ); + } + } + + return (fundAmounts[0], distribution); } } + return super.getExpectedReturn( fromToken, toToken, @@ -147,6 +171,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { disableFlags ); } + } diff --git a/contracts/interface/ISmartTokenConverter.sol b/contracts/interface/ISmartTokenConverter.sol index 45e8927..f2eb66f 100644 --- a/contracts/interface/ISmartTokenConverter.sol +++ b/contracts/interface/ISmartTokenConverter.sol @@ -1,10 +1,23 @@ pragma solidity ^0.5.0; +pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - interface ISmartTokenConverter { - function getReserveRatio(IERC20 token) external view returns (uint32); + + struct Reserve { + uint256 virtualBalance; // reserve virtual balance + uint32 ratio; // reserve ratio, represented in ppm, 1-1000000 + bool isVirtualBalanceEnabled; // true if virtual balance is enabled, false if not + bool isSaleEnabled; // is sale of the reserve token enabled, can be set by the owner + bool isSet; // used to tell if the mapping element is defined + } + + function version() external view returns (uint16); + + function reserves(address) external view returns (Reserve memory); + + function getReserveRatio(IERC20 token) external view returns (uint256); function connectorTokenCount() external view returns (uint256); diff --git a/test/OneSplit.js b/test/OneSplit.js index 7c73033..ed36dc9 100644 --- a/test/OneSplit.js +++ b/test/OneSplit.js @@ -1,11 +1,33 @@ -// const { expectRevert } = require('openzeppelin-test-helpers'); -// const { expect } = require('chai'); +const { expectRevert } = require('openzeppelin-test-helpers'); +const { expect } = require('chai'); const assert = require('assert'); const OneSplitViewMock = artifacts.require('OneSplitViewMock'); const OneSplitMock = artifacts.require('OneSplitMock'); +const OneSplitSmartTokenView = artifacts.require('OneSplitSmartTokenView'); contract('OneSplit', function ([_, addr1]) { + + describe('OneSplitSmartContract', async function () { + beforeEach('should be ok', async function () { + this.smartTokenView = await OneSplitSmartTokenView.new(); + }); + + it('should work', async function () { + const res = await this.smartTokenView.getExpectedReturn( + '0x0000000000000000000000000000000000000000', // ETH + '0x482c31355F4f7966fFcD38eC5c9635ACAe5F4D4F', // Ether Token Smart Relay Token (ETHUSDB) + '0x' + (10).toString(16), + '0x0', + '0x' + Number(web3.utils.toWei('20')).toString(16) + ); + + console.log(res['0'].toString()); + console.log(res['1'].map(x => x.toString())); + }); + + }); + describe('OneSplit', async function () { beforeEach('should be ok', async function () { this.splitView = await OneSplitViewMock.new(); From 79074c36e05b1ec64b8f2d7facc804479799686d Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 31 Mar 2020 18:02:45 +0300 Subject: [PATCH 02/60] fix --- contracts/OneSplitSmartToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 681f834..1ea899e 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -87,7 +87,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { uint256 returnAmount = 0; for (uint256 i = 0; i < tokens.tokens.length; i++) { uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( - toToken.totalSupply(), + fromToken.totalSupply(), tokens.tokens[i].balanceOf(address(converter)), uint32(tokens.totalRatio), amount From bea92a7c6312e08b739a37ccf97a253e47f00a40 Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 31 Mar 2020 22:06:41 +0300 Subject: [PATCH 03/60] done with SmartTokenView contract --- OneSplit.full.bin | 2 +- OneSplit.full.sol | 351 ++++++++++++++++++- contracts/OneSplit.sol | 7 +- contracts/OneSplitSmartToken.sol | 259 +++++++++----- contracts/interface/ISmartTokenConverter.sol | 3 +- test/OneSplit.js | 17 +- 6 files changed, 535 insertions(+), 104 deletions(-) diff --git a/OneSplit.full.bin b/OneSplit.full.bin index eb657f0..f5d9a61 100644 --- a/OneSplit.full.bin +++ b/OneSplit.full.bin @@ -1 +1 @@ -6080604052600080546001600160a01b0319908116736b175474e89094c44da98b954eedeac495271d0f1790915560018054821673a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4817905560028054821673dac17f958d2ee523a2206206994597c13d831ec71790556003805482166e085d4780b73119b644ae5ecd22b376179055600480548216734fabb145d64652a948d72533023f6e7a623c7c531790556005805482167357ab1ec28d129707052df4df418d58a2d46d5f5117905560068054821673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc217905560078054821673c0829421c1d260bd3cb3e0f06cfe2d52db2ce31517905560088054821673818e6fecd516ecc3849daf6845e3ec868087b75517905560098054821673c0a47dfe034b400b47bdad5fecda2621de6c4d95179055600a805482167352ae12abe5d8bd778bd5397f99ca900624cfadd4179055600b80548216736f0cd8c4f6f06eab664c7e3031909452b4b72861179055600c8054821673794e6e91555438afc3ccf1c5076a74f42133d08d179055600d8054821673a2b47e3d5c44877cca798226b7b8118f9bfb7a56179055600e805482167352ea46506b9cc5ef470c5bf89f17dc28bb35d85c179055600f805482167345f783cce6b7ff23b2ab2d70e416cdb7d6055f511790556010805482167379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27179055601180548216733b12e1fbb468bea80b492d635976809bf950186c1790556012805482167306af07097c9eeb7fd685c692751d5c66db49c215179055601380548216736a4ffaafa8dd400676df8076ad6c724867b0e2e817905560148054821673b683d83a532e2cb7dfa5275eed3698436371cc9f17905560158054821673398ec7346dcd622edc5ae82352f02be94c62d119179055601680548216733d9819210a31b4961b30ef54be2aed79b9c9cd3b17905560178054909116734ddc2d193948926d02f9b1fe9e1daa0718270ed51790553480156102e757600080fd5b50604051620054c7380380620054c78339818101604052602081101561030c57600080fd5b5051601880546001600160a01b0319166001600160a01b0390921691909117905561518a806200033d6000396000f3fe6080604052600436106103355760003560e01c806372b6f1bf116101ab578063c762a46c116100f7578063dc1536b211610095578063f4b9fa751161006f578063f4b9fa75146109cd578063f56e281f146109e2578063f69e2046146109f7578063fbe4ed9514610a0c57610335565b8063dc1536b214610851578063e2a7515e14610866578063f484966b1461092e57610335565b8063c9b42c67116100d1578063c9b42c67146107fd578063cede5f6a14610812578063d393c3e914610827578063d77366a41461083c57610335565b8063c762a46c146107be578063c77b9de6146107d3578063c9257775146107e857610335565b80638bdb2afa11610164578063b0a7ef291161013e578063b0a7ef291461076a578063b3bc78441461077f578063b69d045614610794578063c11f4f11146107a957610335565b80638bdb2afa1461072b578063a1b4d01114610740578063a734f06e1461075557610335565b806372b6f1bf1461068f57806375a8b012146106c257806375b5be2d146106d75780637a88bdbd146106ec578063819faf7b14610701578063851954fa1461071657610335565b80633e413bee116102855780635aa8fb481161022357806364ec4e5c116101fd57806364ec4e5c146106085780636b5a4ca21461061d5780636b9589aa146106325780636cbc4a6e1461067a57610335565b80635aa8fb48146105c95780635ae51b82146105de5780635c0cb479146105f357610335565b806344211d621161025f57806344211d62146105755780634a7101d51461058a5780634b57b0be1461059f57806351f1985c146105b457610335565b80633e413bee146105185780634037f9671461052d578063423d03f91461056057610335565b806322320c98116102f25780632f48ab7d116102cc5780632f48ab7d146104c457806334b4dabb146104d9578063372a26cb146104ee5780633ca5b2341461050357610335565b806322320c98146104855780632d3b52071461049a5780632e707bd2146104af57610335565b8063085e2c5b1461034457806312dea160146103ee5780631388b4201461041f57806313989140146104345780632113240d1461045b57806321a360f514610470575b3332141561034257600080fd5b005b34801561035057600080fd5b50610393600480360360a081101561036757600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060800135610a21565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b838110156103d95781810151838201526020016103c1565b50505050905001935050505060405180910390f35b3480156103fa57600080fd5b50610403610b69565b604080516001600160a01b039092168252519081900360200190f35b34801561042b57600080fd5b50610403610b78565b34801561044057600080fd5b50610449610b87565b60408051918252519081900360200190f35b34801561046757600080fd5b50610449610b8d565b34801561047c57600080fd5b50610449610b93565b34801561049157600080fd5b50610403610b9c565b3480156104a657600080fd5b50610449610bab565b3480156104bb57600080fd5b50610449610bb3565b3480156104d057600080fd5b50610403610bb8565b3480156104e557600080fd5b50610449610bc7565b3480156104fa57600080fd5b50610403610bcc565b34801561050f57600080fd5b50610403610bdb565b34801561052457600080fd5b50610403610bea565b34801561053957600080fd5b506104036004803603602081101561055057600080fd5b50356001600160a01b0316610bf9565b34801561056c57600080fd5b50610403610f71565b34801561058157600080fd5b50610449610f80565b34801561059657600080fd5b50610449610f85565b3480156105ab57600080fd5b50610403610f8a565b3480156105c057600080fd5b50610403610f99565b3480156105d557600080fd5b50610449610fa8565b3480156105ea57600080fd5b50610449610fae565b3480156105ff57600080fd5b50610449610fb4565b34801561061457600080fd5b50610449610fb9565b34801561062957600080fd5b50610403610fc0565b610342600480360360c081101561064857600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060808101359060a00135610fcf565b34801561068657600080fd5b50610449610ff8565b34801561069b57600080fd5b50610403600480360360208110156106b257600080fd5b50356001600160a01b0316610fff565b3480156106ce57600080fd5b5061044961124f565b3480156106e357600080fd5b50610403611255565b3480156106f857600080fd5b50610449611264565b34801561070d57600080fd5b50610403611269565b34801561072257600080fd5b50610403611278565b34801561073757600080fd5b50610403611287565b34801561074c57600080fd5b50610403611296565b34801561076157600080fd5b506104036112a5565b34801561077657600080fd5b506104496112bd565b34801561078b57600080fd5b506104496112c3565b3480156107a057600080fd5b506104036112cc565b3480156107b557600080fd5b506104036112db565b3480156107ca57600080fd5b506104496112ea565b3480156107df57600080fd5b506104496112ef565b3480156107f457600080fd5b506104036112f5565b34801561080957600080fd5b50610449611304565b34801561081e57600080fd5b5061040361130b565b34801561083357600080fd5b5061044961131a565b34801561084857600080fd5b50610403611321565b34801561085d57600080fd5b50610449611330565b610342600480360360c081101561087c57600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135600160201b8111156108bb57600080fd5b8201836020820111156108cd57600080fd5b803590602001918460208302840111600160201b831117156108ee57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250611336915050565b34801561093a57600080fd5b5061097d600480360360a081101561095157600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060800135611413565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156109b95781810151838201526020016109a1565b505050509050019250505060405180910390f35b3480156109d957600080fd5b506104036114a7565b3480156109ee57600080fd5b506104496114b6565b348015610a0357600080fd5b506104036114bb565b348015610a1857600080fd5b506104036114ca565b6018546040805163085e2c5b60e01b81526001600160a01b03888116600483015287811660248301526044820187905260648201869052608482018590529151600093606093169163085e2c5b9160a48083019287929190829003018186803b158015610a8d57600080fd5b505afa158015610aa1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610aca57600080fd5b815160208301805160405192949293830192919084600160201b821115610af057600080fd5b908301906020820185811115610b0557600080fd5b82518660208202830111600160201b82111715610b2157600080fd5b82525081516020918201928201910280838360005b83811015610b4e578181015183820152602001610b36565b50505050905001604052505050915091509550959350505050565b600a546001600160a01b031681565b600c546001600160a01b031681565b61200081565b61800081565b64020000000081565b6011546001600160a01b031681565b600160201b81565b608081565b6002546001600160a01b031681565b604081565b6010546001600160a01b031681565b6004546001600160a01b031681565b6001546001600160a01b031681565b6000610c0d826001600160a01b03166114d9565b15610c1b5750600019610f6c565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b178152915181516000936060936001600160a01b0388169361138893919290918291908083835b60208310610c865780518252601f199092019160209182019101610c67565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114610ce7576040519150601f19603f3d011682016040523d82523d6000602084013e610cec565b606091505b509150915081610d025760001992505050610f6c565b6000805b6007835103811015610e5b57828160000181518110610d2157fe5b6020910101516001600160f81b031916602360f91b148015610d645750828160010181518110610d4d57fe5b6020910101516001600160f81b031916607560f81b145b8015610d915750828160020181518110610d7a57fe5b6020910101516001600160f81b031916601b60fa1b145b8015610dbe5750828160030181518110610da757fe5b6020910101516001600160f81b031916606360f81b145b8015610deb5750828160040181518110610dd457fe5b6020910101516001600160f81b031916603960f91b145b8015610e185750828160050181518110610e0157fe5b6020910101516001600160f81b031916607560f81b145b8015610e455750828160060181518110610e2e57fe5b6020910101516001600160f81b031916606d60f81b145b15610e535760019150610e5b565b600101610d06565b5080610e6e576000199350505050610f6c565b60408051600481526024810182526020810180516001600160e01b031663797bf38560e01b178152915181516001600160a01b038916936113889392918291908083835b60208310610ed15780518252601f199092019160209182019101610eb2565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114610f32576040519150601f19603f3d011682016040523d82523d6000602084013e610f37565b606091505b50909350915082610f4f576000199350505050610f6c565b818060200190516020811015610f6457600080fd5b505193505050505b919050565b600f546001600160a01b031681565b601081565b602081565b6006546001600160a01b031681565b600d546001600160a01b031681565b61400081565b61080081565b600881565b6202000081565b6014546001600160a01b031681565b6060610fde8787878686610a21565b915050610fef878787878587611336565b50505050505050565b6208000081565b6000611013826001600160a01b03166114d9565b156110215750600019610f6c565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b178152915181516000936060936001600160a01b0388169361138893919290918291908083835b6020831061108c5780518252601f19909201916020918201910161106d565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d80600081146110ed576040519150601f19603f3d011682016040523d82523d6000602084013e6110f2565b606091505b5091509150816111085760001992505050610f6c565b6000805b60048351038110156111da5782816000018151811061112757fe5b6020910101516001600160f81b031916604160f81b14801561116a575082816001018151811061115357fe5b6020910101516001600160f81b031916606160f81b145b8015611197575082816002018151811061118057fe5b6020910101516001600160f81b031916603b60f91b145b80156111c457508281600301815181106111ad57fe5b6020910101516001600160f81b031916606560f81b145b156111d257600191506111da565b60010161110c565b50806111ed576000199350505050610f6c565b60408051600481526024810182526020810180516001600160e01b0316632274683f60e21b178152915181516001600160a01b0389169361138893929182919080838360208310610ed15780518252601f199092019160209182019101610eb2565b61040081565b6003546001600160a01b031681565b600281565b6015546001600160a01b031681565b6007546001600160a01b031681565b6009546001600160a01b031681565b6017546001600160a01b031681565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b6012546001600160a01b031681565b6013546001600160a01b031681565b600181565b61020081565b6005546001600160a01b031681565b6204000081565b600e546001600160a01b031681565b6201000081565b6008546001600160a01b031681565b61010081565b6113516001600160a01b03871633308763ffffffff61151316565b61135e8686868585611632565b60006113796001600160a01b0387163063ffffffff61166516565b9050838110156113ba5760405162461bcd60e51b81526004018080602001828103825260358152602001806150466035913960400191505060405180910390fd5b6113d46001600160a01b038716338363ffffffff61170f16565b50611409336113f26001600160a01b038a163063ffffffff61166516565b6001600160a01b038a16919063ffffffff61170f16565b5050505050505050565b60608260405190808252806020026020018201604052801561143f578160200160208202803883390190505b50905060005b8381101561149d5761147d8787611475876114698a6001880163ffffffff61178d16565b9063ffffffff6117e616565b600187610a21565b5082828151811061148a57fe5b6020908102919091010152600101611445565b5095945050505050565b6000546001600160a01b031681565b600481565b6016546001600160a01b031681565b6018546001600160a01b031681565b60006001600160a01b038216158061150d57506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b92915050565b8061151d5761162c565b611526846114d9565b15611611576001600160a01b038316331480156115435750803410155b61157e5760405162461bcd60e51b815260040180806020018281038252602b8152602001806150aa602b913960400191505060405180910390fd5b6001600160a01b03821630146115c6576040516001600160a01b0383169082156108fc029083906000818181858888f193505050501580156115c4573d6000803e3d6000fd5b505b8034111561160c57336108fc6115e2348463ffffffff61182816565b6040518115909202916000818181858888f1935050505015801561160a573d6000803e3d6000fd5b505b61162c565b61162c6001600160a01b03851684848463ffffffff61186a16565b50505050565b836001600160a01b0316856001600160a01b031614156116515761165e565b61165e85858585856118c4565b5050505050565b6000611670836114d9565b1561168657506001600160a01b0381163161150d565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156116dc57600080fd5b505afa1580156116f0573d6000803e3d6000fd5b505050506040513d602081101561170657600080fd5b50519392505050565b60008161171e57506001611786565b611727846114d9565b15611768576040516001600160a01b0384169083156108fc029084906000818181858888f19350505050158015611762573d6000803e3d6000fd5b50611786565b6117826001600160a01b038516848463ffffffff6118d116565b5060015b9392505050565b60008261179c5750600061150d565b828202828482816117a957fe5b04146117865760405162461bcd60e51b81526004018080602001828103825260218152602001806150d56021913960400191505060405180910390fd5b600061178683836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250611928565b600061178683836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506119ca565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b17905261162c908590611a24565b61165e8585858585611bdc565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611923908490611a24565b505050565b600081836119b45760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015611979578181015183820152602001611961565b50505050905090810190601f1680156119a65780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816119c057fe5b0495945050505050565b60008184841115611a1c5760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315611979578181015183820152602001611961565b505050900390565b611a36826001600160a01b0316611ec5565b611a87576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310611ac55780518252601f199092019160209182019101611aa6565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114611b27576040519150601f19603f3d011682016040523d82523d6000602084013e611b2c565b606091505b509150915081611b83576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b80511561162c57808060200190516020811015611b9f57600080fd5b505161162c5760405162461bcd60e51b815260040180806020018281038252602a8152602001806150f6602a913960400191505060405180910390fd5b836001600160a01b0316856001600160a01b03161415611bfb5761165e565b611c0e816208000063ffffffff611f0116565b611eb8576006546001600160a01b0386811691161415611d2357600654604080516370a0823160e01b815230600482015290516001600160a01b0390921691632e1a7d4d9183916370a0823191602480820192602092909190829003018186803b158015611c7b57600080fd5b505afa158015611c8f573d6000803e3d6000fd5b505050506040513d6020811015611ca557600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015611ce557600080fd5b505af1158015611cf9573d6000803e3d6000fd5b50505050611d1e73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee85858585611f07565b61165e565b6007546001600160a01b0386811691161415611d8c57600754604080516370a0823160e01b815230600482015290516001600160a01b0390921691632e1a7d4d9183916370a0823191602480820192602092909190829003018186803b158015611c7b57600080fd5b6006546001600160a01b0385811691161415611e3157611dc38573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585611bdc565b600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b158015611e1357600080fd5b505af1158015611e27573d6000803e3d6000fd5b505050505061165e565b6007546001600160a01b0385811691161415611eb857611e688573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585611bdc565b600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b158015611e1357600080fd5b61165e8585858585611f07565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590611ef957508115155b949350505050565b16151590565b61165e85858585855b836001600160a01b0316856001600160a01b03161415611f2f5761165e565b611f37614ffb565b611f3f612307565b9050611f538261080063ffffffff611f0116565b6122f25760005b600a811015612112578181600a8110611f6f57fe5b60200201516001600160a01b0316876001600160a01b0316141561210a5760008282600a8110611f9b57fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015611fd857600080fd5b505afa158015611fec573d6000803e3d6000fd5b505050506040513d602081101561200257600080fd5b505190508282600a811061201257fe5b60200201516001600160a01b0316632e1a7d4d876040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561205c57600080fd5b505af1158015612070573d6000803e3d6000fd5b505050506121028188836001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156120cf57600080fd5b505afa1580156120e3573d6000803e3d6000fd5b505050506040513d60208110156120f957600080fd5b50518888611f10565b50505061165e565b600101611f5a565b5060005b600a8110156122f0578181600a811061212b57fe5b60200201516001600160a01b0316866001600160a01b031614156122e85760008282600a811061215757fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561219457600080fd5b505afa1580156121a8573d6000803e3d6000fd5b505050506040513d60208110156121be57600080fd5b505190506121cf8882888888612423565b6121e9818484600a81106121df57fe5b6020020151612430565b8282600a81106121f557fe5b60200201516001600160a01b031663b6b55f25826001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b15801561225e57600080fd5b505afa158015612272573d6000803e3d6000fd5b505050506040513d602081101561228857600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b1580156122c857600080fd5b505af11580156122dc573d6000803e3d6000fd5b5050505050505061165e565b600101612116565b505b6122ff8686868686612423565b505050505050565b61230f614ffb565b5060408051610140810182527316de59092dae5ccf4a1e6439d611fd0653f0bd0181527304aa51bbcb46541455ccf1b8bef2ebc5d3787ec960208201527373a052500105205d34daf004eab301916da8190f918101919091527383f798e925bcd4017eb265844fddabb448f1707d606082015273d6ad7a6750a7593e092a9b218d66c0a814a3436e608082015273f61718057901f84c4eec4339ef8f0d86d2b4560060a08201527304bc0ab673d88ae9dbc9da2380cb6b79c4bca9ae60c082015273c2cb1040220768554cf699b0d863a3cd4324ce3260e082015273e6354ed5bc4b393a5aad09f21c46e101e692d4476101008201527326ea744e5b887e5205727f55dfbe8685e3b2195161012082015290565b61165e85858585856124e9565b612442826001600160a01b03166114d9565b6124e55760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b15801561249757600080fd5b505afa1580156124ab573d6000803e3d6000fd5b505050506040513d60208110156124c157600080fd5b5051901c6124e5576124e56001600160a01b0383168260001963ffffffff61273616565b5050565b836001600160a01b0316856001600160a01b031614156125085761165e565b61251981601063ffffffff611f0116565b6127295761252685612807565b156125dc5760006125368661293a565b9050856001600160a01b031663db006a75856040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561257e57600080fd5b505af1158015612592573d6000803e3d6000fd5b505050506040513d60208110156125a857600080fd5b50600090506125c66001600160a01b0383163063ffffffff61166516565b90506125d582878387876124e9565b505061165e565b6125e584612807565b156127295760006125f58561293a565b90506126048682868686612a61565b600061261f6001600160a01b0383163063ffffffff61166516565b9050612633826001600160a01b03166114d9565b156126a657601760009054906101000a90046001600160a01b03166001600160a01b0316631249c58b826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561268857600080fd5b505af115801561269c573d6000803e3d6000fd5b50505050506125d5565b6126b08287612430565b856001600160a01b031663a0712d68826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156126f657600080fd5b505af115801561270a573d6000803e3d6000fd5b505050506040513d602081101561272057600080fd5b5050505061165e565b61165e8585858585612a61565b61273f836114d9565b611923576000811180156127cd575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561279f57600080fd5b505afa1580156127b3573d6000803e3d6000fd5b505050506040513d60208110156127c957600080fd5b5051115b156127ed576127ed6001600160a01b03841683600063ffffffff612a6e16565b6119236001600160a01b038416838363ffffffff612a6e16565b6017546000906001600160a01b038381169116141561282857506001610f6c565b601654604080516001600160a01b0385811660248084019190915283518084039091018152604490920183526020820180516001600160e01b0316638e8f294b60e01b17815292518251600095606095931693611388939092918291908083835b602083106128a85780518252601f199092019160209182019101612889565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114612909576040519150601f19603f3d011682016040523d82523d6000602084013e61290e565b606091505b50915091508161292357600092505050610f6c565b6000818060200190516040811015610f6457600080fd5b6017546000906001600160a01b038381169116141561295b57506000610f6c565b60408051600481526024810182526020810180516001600160e01b0316636f307dc360e01b178152915181516000936060936001600160a01b0388169361138893919290918291908083835b602083106129c65780518252601f1990920191602091820191016129a7565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114612a27576040519150601f19603f3d011682016040523d82523d6000602084013e612a2c565b606091505b509150915081612a425760001992505050610f6c565b808060200190516020811015612a5757600080fd5b5051949350505050565b61165e8585858585612b81565b801580612af4575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b158015612ac657600080fd5b505afa158015612ada573d6000803e3d6000fd5b505050506040513d6020811015612af057600080fd5b5051155b612b2f5760405162461bcd60e51b81526004018080602001828103825260368152602001806151206036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611923908490611a24565b836001600160a01b0316856001600160a01b03161415612ba05761165e565b612bb181602063ffffffff611f0116565b612e44576000612bc086610bf9565b90506001600160a01b0380821614612d1057612be4816001600160a01b03166114d9565b15612c6a576040805163081a6b2560e41b81523060048201526024810186905290516001600160a01b038816916381a6b2509160448083019260209291908290030181600087803b158015612c3857600080fd5b505af1158015612c4c573d6000803e3d6000fd5b505050506040513d6020811015612c6257600080fd5b50612ce69050565b60408051632770a7eb60e21b81523060048201526024810186905290516001600160a01b03881691639dc29fac9160448083019260209291908290030181600087803b158015612cb957600080fd5b505af1158015612ccd573d6000803e3d6000fd5b505050506040513d6020811015612ce357600080fd5b50505b6000612d016001600160a01b0383163063ffffffff61166516565b90506125d58287838787612e4d565b612d1985610bf9565b90506001600160a01b0380821614612e4257612d388682868686612e4d565b6000612d536001600160a01b0383163063ffffffff61166516565b9050612d67826001600160a01b03166114d9565b15612de95760408051638f6ede1f60e01b815230600482015290516001600160a01b03881691638f6ede1f91849160248082019260209290919082900301818588803b158015612db657600080fd5b505af1158015612dca573d6000803e3d6000fd5b50505050506040513d6020811015612de157600080fd5b506125d59050565b612df38287612430565b604080516340c10f1960e01b81523060048201526024810183905290516001600160a01b038816916340c10f199160448083019260209291908290030181600087803b1580156126f657600080fd5b505b61165e85858585855b61165e85858585855b836001600160a01b0316856001600160a01b03161415612e755761165e565b612e8681608063ffffffff611f0116565b613096576000612e9586610fff565b90506001600160a01b0380821614612f1857856001600160a01b031663db006a75856040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015612eed57600080fd5b505af1158015612f01573d6000803e3d6000fd5b50505050612f128186868686612e56565b5061165e565b612f2185610fff565b90506001600160a01b038082161461309457612f40868286868661309f565b6000612f5b6001600160a01b0383163063ffffffff61166516565b9050612fe082601560009054906101000a90046001600160a01b03166001600160a01b031663f2f4eb266040518163ffffffff1660e01b815260040160206040518083038186803b158015612faf57600080fd5b505afa158015612fc3573d6000803e3d6000fd5b505050506040513d6020811015612fd957600080fd5b5051612430565b6015546001600160a01b039081169063d2d0e066906130009085166114d9565b61300b57600061300d565b825b61301f856001600160a01b03166114d9565b613029578461303f565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e086901b1681526001600160a01b0390921660048301526024820186905261044d604483015251606480830192600092919082900301818588803b1580156122c857600080fd5b505b61165e85858585855b836001600160a01b0316856001600160a01b031614156130be5761165e565b6130d08161040063ffffffff611f0116565b613344576013546001600160a01b03868116911614156132255760135460408051637f8661a160e01b81526004810186905290516001600160a01b0390921691637f8661a19160248082019260009290919082900301818387803b15801561313757600080fd5b505af115801561314b573d6000803e3d6000fd5b5050601454604080516370a0823160e01b81523060048201529051600094506001600160a01b0390921692506370a08231916024808301926020929190829003018186803b15801561319c57600080fd5b505afa1580156131b0573d6000803e3d6000fd5b505050506040513d60208110156131c657600080fd5b50519050801561320c576014546060906131ed906001600160a01b03168784600187610a21565b60145490925061320a91506001600160a01b031687848487611632565b505b600054612f12906001600160a01b03168686868661334d565b6013546001600160a01b0385811691161415613344576000546132559086906001600160a01b031685858561334d565b600054601354613271916001600160a01b039081169116612430565b601354600054604080516370a0823160e01b815230600482015290516001600160a01b039384169363049878f39316916370a08231916024808301926020929190829003018186803b1580156132c657600080fd5b505afa1580156132da573d6000803e3d6000fd5b505050506040513d60208110156132f057600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561333057600080fd5b505af1158015612720573d6000803e3d6000fd5b61165e85858585855b836001600160a01b0316856001600160a01b0316141561336c5761165e565b61337d81604063ffffffff611f0116565b6135a4576012546001600160a01b0386811691161415613487576012546040805163ef693bed60e01b81523060048201526024810186905290516001600160a01b039092169163ef693bed9160448082019260009290919082900301818387803b1580156133ea57600080fd5b505af11580156133fe573d6000803e3d6000fd5b5050600054604080516370a0823160e01b81523060048201529051611d1e94506001600160a01b039092169250879183916370a08231916024808301926020929190829003018186803b15801561345457600080fd5b505afa158015613468573d6000803e3d6000fd5b505050506040513d602081101561347e57600080fd5b505185856135ad565b6012546001600160a01b03858116911614156135a4576000546134b79086906001600160a01b03168585856135ad565b6000546012546134d3916001600160a01b039081169116612430565b601254600054604080516370a0823160e01b8152306004820181905291516001600160a01b0394851694633b4da69f9416916370a08231916024808301926020929190829003018186803b15801561352a57600080fd5b505afa15801561353e573d6000803e3d6000fd5b505050506040513d602081101561355457600080fd5b5051604080516001600160e01b031960e086901b1681526001600160a01b039093166004840152602483019190915251604480830192600092919082900301818387803b15801561333057600080fd5b61165e85858585855b6135bf856001600160a01b03166114d9565b1580156135db57506135d9846001600160a01b03166114d9565b155b80156135f457506135f48161020063ffffffff611f0116565b156136f55760608251604051908082528060200260200182016040528015613626578160200160208202803883390190505b50905060005b835181101561366b5783818151811061364157fe5b602002602001015160ff1682828151811061365857fe5b602090810291909101015260010161362c565b5061368d8673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee868486613a0f565b60005b83518110156136d35760088482815181106136a757fe5b6020026020010151901c60ff168282815181106136c057fe5b6020908102919091010152600101613690565b50612f1273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee86478486613a0f565b6000546001600160a01b0386811691161480159061372157506000546001600160a01b03858116911614155b801561373b575061373b816201000063ffffffff611f0116565b15613897576060825160405190808252806020026020018201604052801561376d578160200160208202803883390190505b50905060005b83518110156137b25783818151811061378857fe5b602002602001015160ff1682828151811061379f57fe5b6020908102919091010152600101613773565b506000546137cd9087906001600160a01b0316868486613a0f565b60005b83518110156138135760088482815181106137e757fe5b6020026020010151901c60ff1682828151811061380057fe5b60209081029190910101526001016137d0565b50600054604080516370a0823160e01b81523060048201529051612f12926001600160a01b031691889183916370a08231916024808301926020929190829003018186803b15801561386457600080fd5b505afa158015613878573d6000803e3d6000fd5b505050506040513d602081101561388e57600080fd5b50518486613a0f565b6001546001600160a01b038681169116148015906138c357506001546001600160a01b03858116911614155b80156138dd57506138dd816202000063ffffffff611f0116565b15613a06576060825160405190808252806020026020018201604052801561390f578160200160208202803883390190505b50905060005b83518110156139545783818151811061392a57fe5b602002602001015160ff1682828151811061394157fe5b6020908102919091010152600101613915565b5060015461396f9087906001600160a01b0316868486613a0f565b60005b83518110156139b557600884828151811061398957fe5b6020026020010151901c60ff168282815181106139a257fe5b6020908102919091010152600101613972565b50600154604080516370a0823160e01b81523060048201529051612f12926001600160a01b031691889183916370a08231916024808301926020929190829003018186803b15801561386457600080fd5b61165e85858585855b836001600160a01b0316856001600160a01b03161415613a2e5761165e565b613a3661501a565b506040805161012081018252613bca8152613e2e6020820152613f9d9181019190915261442360608201526146c8608082015261482860a08201526149aa60c0820152614b7b60e0820152614d50610100820152600080805b6009811015613ae8576000868281518110613aa657fe5b60200260200101511115613ae057613ada868281518110613ac357fe5b602002602001015184614fa190919063ffffffff16565b92508091505b600101613a8f565b5060008211613b285760405162461bcd60e51b815260040180806020018281038252602f81526020018061507b602f913960400191505060405180910390fd5b8560005b6009811015613bbe57868181518110613b4157fe5b602002602001015160001415613b5657613bb6565b6000613b82856114698a8581518110613b6b57fe5b60200260200101518c61178d90919063ffffffff16565b905083821415613b8f5750815b8083039250613bb38b8b83898660098110613ba657fe5b602002015163ffffffff16565b50505b600101613b2c565b50505050505050505050565b600081613bdf6001600160a01b0386166114d9565b613d0257600954604080516303795fb160e11b81526001600160a01b038881166004830152915160009392909216916306f2bf6291602480820192602092909190829003018186803b158015613c3457600080fd5b505afa158015613c48573d6000803e3d6000fd5b505050506040513d6020811015613c5e57600080fd5b505190506001600160a01b03811615613d0057613c7b8682612430565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b158015613cd157600080fd5b505af1158015613ce5573d6000803e3d6000fd5b505050506040513d6020811015613cfb57600080fd5b505191505b505b613d14846001600160a01b03166114d9565b611ef957600954604080516303795fb160e11b81526001600160a01b038781166004830152915160009392909216916306f2bf6291602480820192602092909190829003018186803b158015613d6957600080fd5b505afa158015613d7d573d6000803e3d6000fd5b505050506040513d6020811015613d9357600080fd5b505190506001600160a01b03811615613e2557806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b158015613df557600080fd5b505af1158015613e09573d6000803e3d6000fd5b50505050506040513d6020811015613e2057600080fd5b505191505b50949350505050565b600854600090613e489085906001600160a01b0316612430565b6008546001600160a01b03908116906329589f6190613e689087166114d9565b613e73576000613e75565b835b613e87876001600160a01b03166114d9565b613e915786613ea7565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b85613eba886001600160a01b03166114d9565b613ec45787613eda565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b158015613f7257600080fd5b505af1158015613f86573d6000803e3d6000fd5b50505050506040513d6020811015612a5757600080fd5b6000613fb1846001600160a01b03166114d9565b1561402057600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561400657600080fd5b505af115801561401a573d6000803e3d6000fd5b50505050505b600a5460408051632ecd14d360e21b81526c42616e636f724e6574776f726b60981b600482015290516000926001600160a01b03169163bb34534c916024808301926020929190829003018186803b15801561407b57600080fd5b505afa15801561408f573d6000803e3d6000fd5b505050506040513d60208110156140a557600080fd5b5051600b549091506060906001600160a01b03908116906375e1cc82906140cd9089166114d9565b6140d757876140e4565b6007546001600160a01b03165b6140f6886001600160a01b03166114d9565b614100578761410d565b6007546001600160a01b03165b6040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b03168152602001826001600160a01b03166001600160a01b031681526020019250505060006040518083038186803b15801561416c57600080fd5b505afa158015614180573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156141a957600080fd5b8101908080516040519392919084600160201b8211156141c857600080fd5b9083019060208201858111156141dd57600080fd5b82518660208202830111600160201b821117156141f957600080fd5b82525081516020918201928201910280838360005b8381101561422657818101518382015260200161420e565b50505050905001604052505050905061426761424a876001600160a01b03166114d9565b6142545786614261565b6007546001600160a01b03165b83612430565b6000826001600160a01b031663c7ba24bc838760016040518463ffffffff1660e01b81526004018080602001848152602001838152602001828103825285818151815260200191508051906020019060200280838360005b838110156142d75781810151838201526020016142bf565b50505050905001945050505050602060405180830381600087803b1580156142fe57600080fd5b505af1158015614312573d6000803e3d6000fd5b505050506040513d602081101561432857600080fd5b5051905061433e6001600160a01b0387166114d9565b1561441957600754604080516370a0823160e01b815230600482015290516001600160a01b0390921691632e1a7d4d9183916370a0823191602480820192602092909190829003018186803b15801561439657600080fd5b505afa1580156143aa573d6000803e3d6000fd5b505050506040513d60208110156143c057600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561440057600080fd5b505af1158015614414573d6000803e3d6000fd5b505050505b9695505050505050565b6000614437846001600160a01b03166114d9565b156144a657600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561448c57600080fd5b505af11580156144a0573d6000803e3d6000fd5b50505050505b6144e36144bb856001600160a01b03166114d9565b6144c557846144d2565b6006546001600160a01b03165b600c546001600160a01b0316612430565b600c546000906001600160a01b0390811690630621b4f6906145069088166114d9565b614510578661451d565b6006546001600160a01b03165b85614530886001600160a01b03166114d9565b61453a5787614547565b6006546001600160a01b03165b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b1580156145a557600080fd5b505af11580156145b9573d6000803e3d6000fd5b505050506040513d60208110156145cf57600080fd5b505190506145e56001600160a01b0385166114d9565b15611ef957600654604080516370a0823160e01b815230600482015290516001600160a01b0390921691632e1a7d4d9183916370a0823191602480820192602092909190829003018186803b15801561463d57600080fd5b505afa158015614651573d6000803e3d6000fd5b505050506040513d602081101561466757600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b1580156146a757600080fd5b505af11580156146bb573d6000803e3d6000fd5b5050505090509392505050565b60015460009081906001600160a01b038681169116146146e95760006146ec565b60025b6000546001600160a01b0387811691161461470857600061470b565b60015b600154910160ff1691506000906001600160a01b0390811690861614614732576000614735565b60025b6000546001600160a01b03878116911614614751576000614754565b60015b0160ff16905081600f0b6000148061476f575080600f0b6000145b1561477f57600092505050611786565b600d546147969087906001600160a01b0316612430565b600d5460408051635320bf6b60e11b8152600019808601600f90810b810b6004840152908501810b900b60248201526044810187905260006064820181905291516001600160a01b039093169263a6417ed69260848084019391929182900301818387803b15801561480757600080fd5b505af115801561481b573d6000803e3d6000fd5b5050505050509392505050565b60025460009081906001600160a01b0386811691161461484957600061484c565b60035b6001546001600160a01b0387811691161461486857600061486b565b60025b6000546001600160a01b0388811691161461488757600061488a565b60015b60025491019190910160ff1691506000906001600160a01b03908116908616146148b55760006148b8565b60035b6001546001600160a01b038781169116146148d45760006148d7565b60025b6000546001600160a01b038881169116146148f35760006148f6565b60015b010160ff16905081600f0b60001480614912575080600f0b6000145b1561492257600092505050611786565b600e546149399087906001600160a01b0316612430565b600e5460408051635320bf6b60e11b8152600019808601600f90810b810b6004840152908501810b900b60248201526044810187905260006064820181905291516001600160a01b039093169263a6417ed69260848084019391929182900301818387803b15801561480757600080fd5b60035460009081906001600160a01b038681169116146149cb5760006149ce565b60045b6002546001600160a01b038781169116146149ea5760006149ed565b60035b6001546001600160a01b03888116911614614a09576000614a0c565b60025b6000546001600160a01b03898116911614614a28576000614a2b565b60015b01010160ff1690506000600360009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b031614614a6a576000614a6d565b60045b6002546001600160a01b03878116911614614a89576000614a8c565b60035b6001546001600160a01b03888116911614614aa8576000614aab565b60025b6000546001600160a01b03898116911614614ac7576000614aca565b60015b01010160ff16905081600f0b60001480614ae7575080600f0b6000145b15614af757600092505050611786565b600f54614b0e9087906001600160a01b0316612430565b600f805460408051635320bf6b60e11b8152600019808701850b850b60048301528501840b90930b60248401526044830187905260006064840181905290516001600160a01b039092169263a6417ed6926084808301939282900301818387803b15801561480757600080fd5b60045460009081906001600160a01b03868116911614614b9c576000614b9f565b60045b6002546001600160a01b03878116911614614bbb576000614bbe565b60035b6001546001600160a01b03888116911614614bda576000614bdd565b60025b6000546001600160a01b03898116911614614bf9576000614bfc565b60015b01010160ff1690506000600460009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b031614614c3b576000614c3e565b60045b6002546001600160a01b03878116911614614c5a576000614c5d565b60035b6001546001600160a01b03888116911614614c79576000614c7c565b60025b6000546001600160a01b03898116911614614c98576000614c9b565b60015b01010160ff16905081600f0b60001480614cb8575080600f0b6000145b15614cc857600092505050611786565b601054614cdf9087906001600160a01b0316612430565b60105460408051635320bf6b60e11b8152600019808601600f90810b810b6004840152908501810b900b60248201526044810187905260006064820181905291516001600160a01b039093169263a6417ed69260848084019391929182900301818387803b15801561480757600080fd5b60055460009081906001600160a01b03868116911614614d71576000614d74565b60055b6003546001600160a01b03878116911614614d90576000614d93565b60045b6002546001600160a01b03888116911614614daf576000614db2565b60035b6001546001600160a01b03898116911614614dce576000614dd1565b60025b6000546001600160a01b038a8116911614614ded576000614df0565b60015b0101010160ff1690506000600560009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b031614614e30576000614e33565b60055b6003546001600160a01b03878116911614614e4f576000614e52565b60045b6002546001600160a01b03888116911614614e6e576000614e71565b60035b6001546001600160a01b03898116911614614e8d576000614e90565b60025b6000546001600160a01b038a8116911614614eac576000614eaf565b60015b0101010160ff16905081600f0b60001480614ecd575080600f0b6000145b15614edd57600092505050611786565b6005546001600160a01b03878116911614801590614f0957506005546001600160a01b03868116911614155b15614f1957600092505050611786565b601154614f309087906001600160a01b0316612430565b60115460408051635320bf6b60e11b8152600019808601600f90810b810b6004840152908501810b900b60248201526044810187905260006064820181905291516001600160a01b039093169263a6417ed69260848084019391929182900301818387803b15801561480757600080fd5b600082820183811015611786576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b604051806101400160405280600a906020820280388339509192915050565b6040518061012001604052806009905b61504381526020019060019003908161502a5790505090565bfefe4f6e6553706c69743a2061637475616c2072657475726e20616d6f756e74206973206c657373207468616e206d696e52657475726e4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f7357726f6e6720757365616765206f66204554482e756e6976657273616c5472616e7366657246726f6d2829536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a265627a7a7231582001f21d116d2ea626b8575e5d196e7506ad4c83ccb78b0379563a8fd6bff6c1f964736f6c63430005100032 \ No newline at end of file +6080604052600080546001600160a01b0319908116736b175474e89094c44da98b954eedeac495271d0f1790915560018054821673a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4817905560028054821673dac17f958d2ee523a2206206994597c13d831ec71790556003805482166e085d4780b73119b644ae5ecd22b376179055600480548216734fabb145d64652a948d72533023f6e7a623c7c531790556005805482167357ab1ec28d129707052df4df418d58a2d46d5f5117905560068054821673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc217905560078054821673c0829421c1d260bd3cb3e0f06cfe2d52db2ce31517905560088054821673818e6fecd516ecc3849daf6845e3ec868087b75517905560098054821673c0a47dfe034b400b47bdad5fecda2621de6c4d95179055600a805482167352ae12abe5d8bd778bd5397f99ca900624cfadd4179055600b80548216736f0cd8c4f6f06eab664c7e3031909452b4b72861179055600c8054821673794e6e91555438afc3ccf1c5076a74f42133d08d179055600d8054821673a2b47e3d5c44877cca798226b7b8118f9bfb7a56179055600e805482167352ea46506b9cc5ef470c5bf89f17dc28bb35d85c179055600f805482167345f783cce6b7ff23b2ab2d70e416cdb7d6055f511790556010805482167379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27179055601180548216733b12e1fbb468bea80b492d635976809bf950186c1790556012805482167306af07097c9eeb7fd685c692751d5c66db49c215179055601380548216736a4ffaafa8dd400676df8076ad6c724867b0e2e817905560148054821673b683d83a532e2cb7dfa5275eed3698436371cc9f17905560158054821673398ec7346dcd622edc5ae82352f02be94c62d119179055601680548216733d9819210a31b4961b30ef54be2aed79b9c9cd3b17905560178054909116734ddc2d193948926d02f9b1fe9e1daa0718270ed5179055348015620002e857600080fd5b5060405162005833380380620058338339810160408190526200030b9162000344565b601880546001600160a01b0319166001600160a01b0392909216919091179055620003ad565b80516200033e8162000393565b92915050565b6000602082840312156200035757600080fd5b600062000365848462000331565b949350505050565b60006200033e8262000387565b60006200033e826200036d565b6001600160a01b031690565b6200039e816200037a565b8114620003aa57600080fd5b50565b61547680620003bd6000396000f3fe6080604052600436106103355760003560e01c806372b6f1bf116101ab578063c762a46c116100f7578063dc1536b211610095578063f4b9fa751161006f578063f4b9fa75146107c4578063f56e281f146107d9578063f69e2046146107ee578063fbe4ed951461080357610335565b8063dc1536b21461076f578063e2a7515e14610784578063f484966b1461079757610335565b8063c9b42c67116100d1578063c9b42c671461071b578063cede5f6a14610730578063d393c3e914610745578063d77366a41461075a57610335565b8063c762a46c146106dc578063c77b9de6146106f1578063c92577751461070657610335565b80638bdb2afa11610164578063b0a7ef291161013e578063b0a7ef2914610688578063b3bc78441461069d578063b69d0456146106b2578063c11f4f11146106c757610335565b80638bdb2afa14610649578063a1b4d0111461065e578063a734f06e1461067357610335565b806372b6f1bf146105c057806375a8b012146105e057806375b5be2d146105f55780637a88bdbd1461060a578063819faf7b1461061f578063851954fa1461063457610335565b80633e413bee116102855780635aa8fb481161022357806364ec4e5c116101fd57806364ec4e5c1461056e5780636b5a4ca2146105835780636b9589aa146105985780636cbc4a6e146105ab57610335565b80635aa8fb481461052f5780635ae51b82146105445780635c0cb4791461055957610335565b806344211d621161025f57806344211d62146104db5780634a7101d5146104f05780634b57b0be1461050557806351f1985c1461051a57610335565b80633e413bee146104915780634037f967146104a6578063423d03f9146104c657610335565b806322320c98116102f25780632f48ab7d116102cc5780632f48ab7d1461043d57806334b4dabb14610452578063372a26cb146104675780633ca5b2341461047c57610335565b806322320c98146103fe5780632d3b5207146104135780632e707bd21461042857610335565b8063085e2c5b1461034457806312dea1601461037b5780631388b4201461039d57806313989140146103b25780632113240d146103d457806321a360f5146103e9575b3332141561034257600080fd5b005b34801561035057600080fd5b5061036461035f366004614a5f565b610818565b6040516103729291906152dd565b60405180910390f35b34801561038757600080fd5b506103906108b8565b604051610372919061509f565b3480156103a957600080fd5b506103906108c7565b3480156103be57600080fd5b506103c76108d6565b60405161037291906152cf565b3480156103e057600080fd5b506103c76108dc565b3480156103f557600080fd5b506103c76108e2565b34801561040a57600080fd5b506103906108eb565b34801561041f57600080fd5b506103c76108fa565b34801561043457600080fd5b506103c7610903565b34801561044957600080fd5b50610390610908565b34801561045e57600080fd5b506103c7610917565b34801561047357600080fd5b5061039061091c565b34801561048857600080fd5b5061039061092b565b34801561049d57600080fd5b5061039061093a565b3480156104b257600080fd5b506103906104c1366004614984565b610949565b3480156104d257600080fd5b50610390610c32565b3480156104e757600080fd5b506103c7610c41565b3480156104fc57600080fd5b506103c7610c46565b34801561051157600080fd5b50610390610c4b565b34801561052657600080fd5b50610390610c5a565b34801561053b57600080fd5b506103c7610c69565b34801561055057600080fd5b506103c7610c6f565b34801561056557600080fd5b506103c7610c75565b34801561057a57600080fd5b506103c7610c7a565b34801561058f57600080fd5b50610390610c81565b6103426105a6366004614ad4565b610c90565b3480156105b757600080fd5b506103c7610cb9565b3480156105cc57600080fd5b506103906105db366004614984565b610cc0565b3480156105ec57600080fd5b506103c7610eaa565b34801561060157600080fd5b50610390610eb0565b34801561061657600080fd5b506103c7610ebf565b34801561062b57600080fd5b50610390610ec4565b34801561064057600080fd5b50610390610ed3565b34801561065557600080fd5b50610390610ee2565b34801561066a57600080fd5b50610390610ef1565b34801561067f57600080fd5b50610390610f00565b34801561069457600080fd5b506103c7610f18565b3480156106a957600080fd5b506103c7610f1e565b3480156106be57600080fd5b50610390610f27565b3480156106d357600080fd5b50610390610f36565b3480156106e857600080fd5b506103c7610f45565b3480156106fd57600080fd5b506103c7610f4a565b34801561071257600080fd5b50610390610f50565b34801561072757600080fd5b506103c7610f5f565b34801561073c57600080fd5b50610390610f66565b34801561075157600080fd5b506103c7610f75565b34801561076657600080fd5b50610390610f7c565b34801561077b57600080fd5b506103c7610f8b565b6103426107923660046149c0565b610f91565b3480156107a357600080fd5b506107b76107b2366004614a5f565b611058565b604051610372919061508e565b3480156107d057600080fd5b506103906110ec565b3480156107e557600080fd5b506103c76110fb565b3480156107fa57600080fd5b50610390611100565b34801561080f57600080fd5b5061039061110f565b60185460405163085e2c5b60e01b81526000916060916001600160a01b039091169063085e2c5b90610856908a908a908a908a908a906004016150c8565b60006040518083038186803b15801561086e57600080fd5b505afa158015610882573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526108aa9190810190614b5b565b915091509550959350505050565b600a546001600160a01b031681565b600c546001600160a01b031681565b61200081565b61800081565b64020000000081565b6011546001600160a01b031681565b64010000000081565b608081565b6002546001600160a01b031681565b604081565b6010546001600160a01b031681565b6004546001600160a01b031681565b6001546001600160a01b031681565b600061095d826001600160a01b031661111e565b1561096b5750600019610c2d565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b17905290516000916060916001600160a01b03861691611388916109b49190614fbf565b6000604051808303818686fa925050503d80600081146109f0576040519150601f19603f3d011682016040523d82523d6000602084013e6109f5565b606091505b509150915081610a0b5760001992505050610c2d565b6000805b6007835103811015610b6457828160000181518110610a2a57fe5b6020910101516001600160f81b031916602360f91b148015610a6d5750828160010181518110610a5657fe5b6020910101516001600160f81b031916607560f81b145b8015610a9a5750828160020181518110610a8357fe5b6020910101516001600160f81b031916601b60fa1b145b8015610ac75750828160030181518110610ab057fe5b6020910101516001600160f81b031916606360f81b145b8015610af45750828160040181518110610add57fe5b6020910101516001600160f81b031916603960f91b145b8015610b215750828160050181518110610b0a57fe5b6020910101516001600160f81b031916607560f81b145b8015610b4e5750828160060181518110610b3757fe5b6020910101516001600160f81b031916606d60f81b145b15610b5c5760019150610b64565b600101610a0f565b5080610b77576000199350505050610c2d565b60408051600481526024810182526020810180516001600160e01b031663797bf38560e01b17905290516001600160a01b0387169161138891610bba9190614fbf565b6000604051808303818686fa925050503d8060008114610bf6576040519150601f19603f3d011682016040523d82523d6000602084013e610bfb565b606091505b50909350915082610c13576000199350505050610c2d565b81806020019051610c2791908101906149a2565b93505050505b919050565b600f546001600160a01b031681565b601081565b602081565b6006546001600160a01b031681565b600d546001600160a01b031681565b61400081565b61080081565b600881565b6202000081565b6014546001600160a01b031681565b6060610c9f8787878686610818565b915050610cb0878787878587610f91565b50505050505050565b6208000081565b6000610cd4826001600160a01b031661111e565b15610ce25750600019610c2d565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b17905290516000916060916001600160a01b0386169161138891610d2b9190614fbf565b6000604051808303818686fa925050503d8060008114610d67576040519150601f19603f3d011682016040523d82523d6000602084013e610d6c565b606091505b509150915081610d825760001992505050610c2d565b6000805b6004835103811015610e5457828160000181518110610da157fe5b6020910101516001600160f81b031916604160f81b148015610de45750828160010181518110610dcd57fe5b6020910101516001600160f81b031916606160f81b145b8015610e115750828160020181518110610dfa57fe5b6020910101516001600160f81b031916603b60f91b145b8015610e3e5750828160030181518110610e2757fe5b6020910101516001600160f81b031916606560f81b145b15610e4c5760019150610e54565b600101610d86565b5080610e67576000199350505050610c2d565b60408051600481526024810182526020810180516001600160e01b0316632274683f60e21b17905290516001600160a01b0387169161138891610bba9190614fbf565b61040081565b6003546001600160a01b031681565b600281565b6015546001600160a01b031681565b6007546001600160a01b031681565b6009546001600160a01b031681565b6017546001600160a01b031681565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b6012546001600160a01b031681565b6013546001600160a01b031681565b600181565b61020081565b6005546001600160a01b031681565b6204000081565b600e546001600160a01b031681565b6201000081565b6008546001600160a01b031681565b61010081565b610fac6001600160a01b03871633308763ffffffff61115816565b610fb98686868585611258565b6000610fd46001600160a01b0387163063ffffffff61128b16565b905083811015610fff5760405162461bcd60e51b8152600401610ff690615242565b60405180910390fd5b6110196001600160a01b038716338363ffffffff61132f16565b5061104e336110376001600160a01b038a163063ffffffff61128b16565b6001600160a01b038a16919063ffffffff61132f16565b5050505050505050565b606082604051908082528060200260200182016040528015611084578160200160208202803883390190505b50905060005b838110156110e2576110c287876110ba876110ae8a6001880163ffffffff6113ac16565b9063ffffffff6113e616565b600187610818565b508282815181106110cf57fe5b602090810291909101015260010161108a565b5095945050505050565b6000546001600160a01b031681565b600481565b6016546001600160a01b031681565b6018546001600160a01b031681565b60006001600160a01b038216158061115257506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b92915050565b8061116257611252565b61116b8461111e565b15611237576001600160a01b038316331480156111885750803410155b6111a45760405162461bcd60e51b8152600401610ff690615272565b6001600160a01b03821630146111ec576040516001600160a01b0383169082156108fc029083906000818181858888f193505050501580156111ea573d6000803e3d6000fd5b505b8034111561123257336108fc611208348463ffffffff61142816565b6040518115909202916000818181858888f19350505050158015611230573d6000803e3d6000fd5b505b611252565b6112526001600160a01b03851684848463ffffffff61146a16565b50505050565b836001600160a01b0316856001600160a01b0316141561127757611284565b61128485858585856114c5565b5050505050565b60006112968361111e565b156112ac57506001600160a01b03811631611152565b6040516370a0823160e01b81526001600160a01b038416906370a08231906112d8908590600401614fcb565b60206040518083038186803b1580156112f057600080fd5b505afa158015611304573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506113289190810190614b3d565b9392505050565b60008161133e57506001611328565b6113478461111e565b15611388576040516001600160a01b0384169083156108fc029084906000818181858888f19350505050158015611382573d6000803e3d6000fd5b50611328565b6113a26001600160a01b038516848463ffffffff6114d216565b5060019392505050565b6000826113bb57506000611152565b828202828482816113c857fe5b04146113285760405162461bcd60e51b8152600401610ff690615282565b600061132883836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506114f9565b600061132883836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250611530565b6040516112529085906323b872dd60e01b9061148e9087908790879060240161502b565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261155c565b6112848585858585611641565b6040516114f490849063a9059cbb60e01b9061148e9086908690602401615053565b505050565b6000818361151a5760405162461bcd60e51b8152600401610ff69190615221565b50600083858161152657fe5b0495945050505050565b600081848411156115545760405162461bcd60e51b8152600401610ff69190615221565b505050900390565b61156e826001600160a01b0316611910565b61158a5760405162461bcd60e51b8152600401610ff6906152bf565b60006060836001600160a01b0316836040516115a69190614fbf565b6000604051808303816000865af19150503d80600081146115e3576040519150601f19603f3d011682016040523d82523d6000602084013e6115e8565b606091505b50915091508161160a5760405162461bcd60e51b8152600401610ff690615252565b8051156112525780806020019051611625919081019061492c565b6112525760405162461bcd60e51b8152600401610ff690615292565b836001600160a01b0316856001600160a01b0316141561166057611284565b611673816208000063ffffffff61194c16565b611903576006546001600160a01b0386811691161415611789576006546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a08231906116c5903090600401614fd9565b60206040518083038186803b1580156116dd57600080fd5b505afa1580156116f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117159190810190614b3d565b6040518263ffffffff1660e01b815260040161173191906152cf565b600060405180830381600087803b15801561174b57600080fd5b505af115801561175f573d6000803e3d6000fd5b5050505061178473eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee85858585611952565b611284565b6007546001600160a01b03868116911614156117d7576007546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a08231906116c5903090600401614fd9565b6006546001600160a01b038581169116141561187c5761180e8573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585611641565b600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b15801561185e57600080fd5b505af1158015611872573d6000803e3d6000fd5b5050505050611284565b6007546001600160a01b0385811691161415611903576118b38573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585611641565b600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b15801561185e57600080fd5b6112848585858585611952565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061194457508115155b949350505050565b16151590565b61128485858585855b836001600160a01b0316856001600160a01b0316141561197a57611284565b6119826146ee565b61198a611d56565b905061199e8261080063ffffffff61194c16565b611d415760005b600a811015611b63578181600a81106119ba57fe5b60200201516001600160a01b0316876001600160a01b03161415611b5b5760008282600a81106119e657fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a2357600080fd5b505afa158015611a37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a5b91908101906149a2565b90508282600a8110611a6957fe5b60200201516001600160a01b0316632e1a7d4d876040518263ffffffff1660e01b8152600401611a9991906152cf565b600060405180830381600087803b158015611ab357600080fd5b505af1158015611ac7573d6000803e3d6000fd5b50505050611b538188836001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611afc9190614fd9565b60206040518083038186803b158015611b1457600080fd5b505afa158015611b28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b4c9190810190614b3d565b888861195b565b505050611284565b6001016119a5565b5060005b600a811015611d3f578181600a8110611b7c57fe5b60200201516001600160a01b0316866001600160a01b03161415611d375760008282600a8110611ba857fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015611be557600080fd5b505afa158015611bf9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c1d91908101906149a2565b9050611c2c8882888888611e72565b611c46818484600a8110611c3c57fe5b6020020151611e7f565b8282600a8110611c5257fe5b60200201516001600160a01b031663b6b55f25826001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611c919190614fd9565b60206040518083038186803b158015611ca957600080fd5b505afa158015611cbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ce19190810190614b3d565b6040518263ffffffff1660e01b8152600401611cfd91906152cf565b600060405180830381600087803b158015611d1757600080fd5b505af1158015611d2b573d6000803e3d6000fd5b50505050505050611284565b600101611b67565b505b611d4e8686868686611e72565b505050505050565b611d5e6146ee565b5060408051610140810182527316de59092dae5ccf4a1e6439d611fd0653f0bd0181527304aa51bbcb46541455ccf1b8bef2ebc5d3787ec960208201527373a052500105205d34daf004eab301916da8190f918101919091527383f798e925bcd4017eb265844fddabb448f1707d606082015273d6ad7a6750a7593e092a9b218d66c0a814a3436e608082015273f61718057901f84c4eec4339ef8f0d86d2b4560060a08201527304bc0ab673d88ae9dbc9da2380cb6b79c4bca9ae60c082015273c2cb1040220768554cf699b0d863a3cd4324ce3260e082015273e6354ed5bc4b393a5aad09f21c46e101e692d4476101008201527326ea744e5b887e5205727f55dfbe8685e3b2195161012082015290565b6112848585858585611f3c565b611e91826001600160a01b031661111e565b611f3857604051636eb1769f60e11b815260ff906001600160a01b0384169063dd62ed3e90611ec69030908690600401614fe7565b60206040518083038186803b158015611ede57600080fd5b505afa158015611ef2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f169190810190614b3d565b901c611f3857611f386001600160a01b0383168260001963ffffffff61219b16565b5050565b836001600160a01b0316856001600160a01b03161415611f5b57611284565b611f6c81601063ffffffff61194c16565b61218e57611f7985612270565b1561203c576000611f898661236e565b60405163db006a7560e01b81529091506001600160a01b0387169063db006a7590611fb89087906004016152cf565b602060405180830381600087803b158015611fd257600080fd5b505af1158015611fe6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061200a9190810190614b3d565b5060006120266001600160a01b0383163063ffffffff61128b16565b90506120358287838787611f3c565b5050611284565b61204584612270565b1561218e5760006120558561236e565b90506120648682868686612443565b600061207f6001600160a01b0383163063ffffffff61128b16565b9050612093826001600160a01b031661111e565b1561210657601760009054906101000a90046001600160a01b03166001600160a01b0316631249c58b826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156120e857600080fd5b505af11580156120fc573d6000803e3d6000fd5b5050505050612035565b6121108287611e7f565b60405163140e25ad60e31b81526001600160a01b0387169063a0712d689061213c9084906004016152cf565b602060405180830381600087803b15801561215657600080fd5b505af115801561216a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b539190810190614b3d565b6112848585858585612443565b6121a48361111e565b6114f4576000811180156122365750604051636eb1769f60e11b81526000906001600160a01b0385169063dd62ed3e906121e4903090879060040161501d565b60206040518083038186803b1580156121fc57600080fd5b505afa158015612210573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506122349190810190614b3d565b115b15612256576122566001600160a01b03841683600063ffffffff61245016565b6114f46001600160a01b038416838363ffffffff61245016565b6017546000906001600160a01b038381169116141561229157506001610c2d565b6016546040516000916060916001600160a01b039091169061138890638e8f294b60e01b906122c490889060240161509f565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516123029190614fbf565b6000604051808303818686fa925050503d806000811461233e576040519150601f19603f3d011682016040523d82523d6000602084013e612343565b606091505b50915091508161235857600092505050610c2d565b6000818060200190516110e2919081019061494a565b6017546000906001600160a01b038381169116141561238f57506000610c2d565b60408051600481526024810182526020810180516001600160e01b0316636f307dc360e01b17905290516000916060916001600160a01b03861691611388916123d89190614fbf565b6000604051808303818686fa925050503d8060008114612414576040519150601f19603f3d011682016040523d82523d6000602084013e612419565b606091505b50915091508161242f5760001992505050610c2d565b8080602001905161194491908101906149a2565b6112848585858585612516565b8015806124d85750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e90612486903090869060040161501d565b60206040518083038186803b15801561249e57600080fd5b505afa1580156124b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506124d69190810190614b3d565b155b6124f45760405162461bcd60e51b8152600401610ff6906152af565b6040516114f490849063095ea7b360e01b9061148e9086908690602401615053565b836001600160a01b0316856001600160a01b0316141561253557611284565b61254681602063ffffffff61194c16565b6127cc57600061255586610949565b90506001600160a01b03808216146126b057612579816001600160a01b031661111e565b156126045760405163081a6b2560e41b81526001600160a01b038716906381a6b250906125ac9030908890600401615002565b602060405180830381600087803b1580156125c657600080fd5b505af11580156125da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506125fe9190810190614b3d565b50612686565b604051632770a7eb60e21b81526001600160a01b03871690639dc29fac906126329030908890600401615002565b602060405180830381600087803b15801561264c57600080fd5b505af1158015612660573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126849190810190614b3d565b505b60006126a16001600160a01b0383163063ffffffff61128b16565b905061203582878387876127d5565b6126b985610949565b90506001600160a01b03808216146127ca576126d886828686866127d5565b60006126f36001600160a01b0383163063ffffffff61128b16565b9050612707826001600160a01b031661111e565b1561279257604051638f6ede1f60e01b81526001600160a01b03871690638f6ede1f90839061273a903090600401614fd9565b6020604051808303818588803b15801561275357600080fd5b505af1158015612767573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525061278c9190810190614b3d565b50612035565b61279c8287611e7f565b6040516340c10f1960e01b81526001600160a01b038716906340c10f199061213c9030908590600401615002565b505b61128485858585855b61128485858585855b836001600160a01b0316856001600160a01b031614156127fd57611284565b61280e81608063ffffffff61194c16565b612a1057600061281d86610cc0565b90506001600160a01b03808216146128a05760405163db006a7560e01b81526001600160a01b0387169063db006a759061285b9087906004016152cf565b600060405180830381600087803b15801561287557600080fd5b505af1158015612889573d6000803e3d6000fd5b5050505061289a81868686866127de565b50611284565b6128a985610cc0565b90506001600160a01b0380821614612a0e576128c88682868686612a19565b60006128e36001600160a01b0383163063ffffffff61128b16565b905061297482601560009054906101000a90046001600160a01b03166001600160a01b031663f2f4eb266040518163ffffffff1660e01b815260040160206040518083038186803b15801561293757600080fd5b505afa15801561294b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061296f91908101906148d9565b611e7f565b6015546001600160a01b039081169063d2d0e0669061299490851661111e565b61299f5760006129a1565b825b6129b3856001600160a01b031661111e565b6129bd57846129d3565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b8461044d6040518563ffffffff1660e01b81526004016129f5939291906151c3565b6000604051808303818588803b158015611d1757600080fd5b505b61128485858585855b836001600160a01b0316856001600160a01b03161415612a3857611284565b612a4a8161040063ffffffff61194c16565b612cd0576013546001600160a01b0386811691161415612ba657601354604051637f8661a160e01b81526001600160a01b0390911690637f8661a190612a949086906004016152cf565b600060405180830381600087803b158015612aae57600080fd5b505af1158015612ac2573d6000803e3d6000fd5b50506014546040516370a0823160e01b8152600093506001600160a01b0390911691506370a0823190612af9903090600401614fd9565b60206040518083038186803b158015612b1157600080fd5b505afa158015612b25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b499190810190614b3d565b90508015612b8d57601454606090612b6e906001600160a01b03168784600187610818565b601454909250612b8b91506001600160a01b031687848487611258565b505b60005461289a906001600160a01b031686868686612cd9565b6013546001600160a01b0385811691161415612cd057600054612bd69086906001600160a01b0316858585612cd9565b600054601354612bf2916001600160a01b039081169116611e7f565b6013546000546040516370a0823160e01b81526001600160a01b039283169263049878f39216906370a0823190612c2d903090600401614fd9565b60206040518083038186803b158015612c4557600080fd5b505afa158015612c59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612c7d9190810190614b3d565b6040518263ffffffff1660e01b8152600401612c9991906152cf565b600060405180830381600087803b158015612cb357600080fd5b505af1158015612cc7573d6000803e3d6000fd5b50505050611284565b61128485858585855b836001600160a01b0316856001600160a01b03161415612cf857611284565b612d0981604063ffffffff61194c16565b612f0d576012546001600160a01b0386811691161415612e165760125460405163ef693bed60e01b81526001600160a01b039091169063ef693bed90612d559030908790600401615002565b600060405180830381600087803b158015612d6f57600080fd5b505af1158015612d83573d6000803e3d6000fd5b50506000546040516370a0823160e01b815261178493506001600160a01b039091169150869082906370a0823190612dbf903090600401614fd9565b60206040518083038186803b158015612dd757600080fd5b505afa158015612deb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e0f9190810190614b3d565b8585612f16565b6012546001600160a01b0385811691161415612f0d57600054612e469086906001600160a01b0316858585612f16565b600054601254612e62916001600160a01b039081169116611e7f565b6012546000546040516370a0823160e01b81526001600160a01b0392831692633b4da69f9230929116906370a0823190612ea0908490600401614fd9565b60206040518083038186803b158015612eb857600080fd5b505afa158015612ecc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612ef09190810190614b3d565b6040518363ffffffff1660e01b8152600401612c99929190615002565b61128485858585855b612f28856001600160a01b031661111e565b158015612f445750612f42846001600160a01b031661111e565b155b8015612f5d5750612f5d8161020063ffffffff61194c16565b1561305e5760608251604051908082528060200260200182016040528015612f8f578160200160208202803883390190505b50905060005b8351811015612fd457838181518110612faa57fe5b602002602001015160ff16828281518110612fc157fe5b6020908102919091010152600101612f95565b50612ff68673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee868486613368565b60005b835181101561303c57600884828151811061301057fe5b6020026020010151901c60ff1682828151811061302957fe5b6020908102919091010152600101612ff9565b5061289a73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee86478486613368565b6000546001600160a01b0386811691161480159061308a57506000546001600160a01b03858116911614155b80156130a457506130a4816201000063ffffffff61194c16565b1561320a57606082516040519080825280602002602001820160405280156130d6578160200160208202803883390190505b50905060005b835181101561311b578381815181106130f157fe5b602002602001015160ff1682828151811061310857fe5b60209081029190910101526001016130dc565b506000546131369087906001600160a01b0316868486613368565b60005b835181101561317c57600884828151811061315057fe5b6020026020010151901c60ff1682828151811061316957fe5b6020908102919091010152600101613139565b506000546040516370a0823160e01b815261289a916001600160a01b031690879082906370a08231906131b3903090600401614fd9565b60206040518083038186803b1580156131cb57600080fd5b505afa1580156131df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506132039190810190614b3d565b8486613368565b6001546001600160a01b0386811691161480159061323657506001546001600160a01b03858116911614155b80156132505750613250816202000063ffffffff61194c16565b1561335f5760608251604051908082528060200260200182016040528015613282578160200160208202803883390190505b50905060005b83518110156132c75783818151811061329d57fe5b602002602001015160ff168282815181106132b457fe5b6020908102919091010152600101613288565b506001546132e29087906001600160a01b0316868486613368565b60005b83518110156133285760088482815181106132fc57fe5b6020026020010151901c60ff1682828151811061331557fe5b60209081029190910101526001016132e5565b506001546040516370a0823160e01b815261289a916001600160a01b031690879082906370a08231906131b3903090600401614fd9565b61128485858585855b836001600160a01b0316856001600160a01b0316141561338757611284565b61338f61470d565b506040805161012081018252613504815261377860208201526138b591810191909152613c426060820152613ed1608082015261401860a082015261416760c082015261430960e08201526144ab610100820152600080805b60098110156134415760008682815181106133ff57fe5b602002602001015111156134395761343386828151811061341c57fe5b6020026020010151846146c990919063ffffffff16565b92508091505b6001016133e8565b50600082116134625760405162461bcd60e51b8152600401610ff690615262565b8560005b60098110156134f85786818151811061347b57fe5b602002602001015160001415613490576134f0565b60006134bc856110ae8a85815181106134a557fe5b60200260200101518c6113ac90919063ffffffff16565b9050838214156134c95750815b80830392506134ed8b8b838986600981106134e057fe5b602002015163ffffffff16565b50505b600101613466565b50505050505050505050565b6000816135196001600160a01b03861661111e565b613641576009546040516303795fb160e11b81526000916001600160a01b0316906306f2bf629061354e90899060040161509f565b60206040518083038186803b15801561356657600080fd5b505afa15801561357a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061359e91908101906149a2565b90506001600160a01b0381161561363f576135b98682611e7f565b6040516395e3c50b60e01b81526001600160a01b038216906395e3c50b906135ea90859060019042906004016152fd565b602060405180830381600087803b15801561360457600080fd5b505af1158015613618573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061363c9190810190614b3d565b91505b505b613653846001600160a01b031661111e565b611944576009546040516303795fb160e11b81526000916001600160a01b0316906306f2bf629061368890889060040161509f565b60206040518083038186803b1580156136a057600080fd5b505afa1580156136b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506136d891908101906149a2565b90506001600160a01b0381161561376f5760405163f39b5b9b60e01b81526001600160a01b0382169063f39b5b9b90849061371a906001904290600401615213565b6020604051808303818588803b15801561373357600080fd5b505af1158015613747573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525061376c9190810190614b3d565b91505b50949350505050565b6008546000906137929085906001600160a01b0316611e7f565b6008546001600160a01b03908116906329589f61906137b290871661111e565b6137bd5760006137bf565b835b6137d1876001600160a01b031661111e565b6137db57866137f1565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b85613804886001600160a01b031661111e565b61380e5787613824565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b30600160ff1b6000734d37f28d2db99e8d35a6c725a5f1749a085850a36040518963ffffffff1660e01b8152600401613863979695949392919061510a565b6020604051808303818588803b15801561387c57600080fd5b505af1158015613890573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506119449190810190614b3d565b60006138c9846001600160a01b031661111e565b1561393857600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561391e57600080fd5b505af1158015613932573d6000803e3d6000fd5b50505050505b600a54604051632ecd14d360e21b81526000916001600160a01b03169063bb34534c90613967906004016152a2565b60206040518083038186803b15801561397f57600080fd5b505afa158015613993573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506139b791908101906148d9565b600b549091506060906001600160a01b03908116906375e1cc82906139dd90891661111e565b6139e757876139f4565b6007546001600160a01b03165b613a06886001600160a01b031661111e565b613a105787613a1d565b6007546001600160a01b03165b6040518363ffffffff1660e01b8152600401613a3a9291906150ad565b60006040518083038186803b158015613a5257600080fd5b505afa158015613a66573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613a8e91908101906148f7565b9050613ac2613aa5876001600160a01b031661111e565b613aaf5786613abc565b6007546001600160a01b03165b83611e7f565b6040516331ee892f60e21b81526000906001600160a01b0384169063c7ba24bc90613af69085908990600190600401615061565b602060405180830381600087803b158015613b1057600080fd5b505af1158015613b24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613b489190810190614b3d565b9050613b5c866001600160a01b031661111e565b15613c38576007546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a0823190613b99903090600401614fd9565b60206040518083038186803b158015613bb157600080fd5b505afa158015613bc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613be99190810190614b3d565b6040518263ffffffff1660e01b8152600401613c0591906152cf565b600060405180830381600087803b158015613c1f57600080fd5b505af1158015613c33573d6000803e3d6000fd5b505050505b9695505050505050565b6000613c56846001600160a01b031661111e565b15613cc557600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015613cab57600080fd5b505af1158015613cbf573d6000803e3d6000fd5b50505050505b613d02613cda856001600160a01b031661111e565b613ce45784613cf1565b6006546001600160a01b03165b600c546001600160a01b0316611e7f565b600c546000906001600160a01b0390811690630621b4f690613d2590881661111e565b613d2f5786613d3c565b6006546001600160a01b03165b85613d4f886001600160a01b031661111e565b613d595787613d66565b6006546001600160a01b03165b60016040518563ffffffff1660e01b8152600401613d879493929190615185565b602060405180830381600087803b158015613da157600080fd5b505af1158015613db5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613dd99190810190614b3d565b9050613ded846001600160a01b031661111e565b15611944576006546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a0823190613e2a903090600401614fd9565b60206040518083038186803b158015613e4257600080fd5b505afa158015613e56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613e7a9190810190614b3d565b6040518263ffffffff1660e01b8152600401613e9691906152cf565b600060405180830381600087803b158015613eb057600080fd5b505af1158015613ec4573d6000803e3d6000fd5b5050505090509392505050565b60015460009081906001600160a01b03868116911614613ef2576000613ef5565b60025b6000546001600160a01b03878116911614613f11576000613f14565b60015b600154910160ff1691506000906001600160a01b0390811690861614613f3b576000613f3e565b60025b6000546001600160a01b03878116911614613f5a576000613f5d565b60015b0160ff16905081600f0b60001480613f78575080600f0b6000145b15613f8857600092505050611328565b600d54613f9f9087906001600160a01b0316611e7f565b600d54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed690613fdd90600019808701919086019089906000906004016151eb565b600060405180830381600087803b158015613ff757600080fd5b505af115801561400b573d6000803e3d6000fd5b5050505050509392505050565b60025460009081906001600160a01b0386811691161461403957600061403c565b60035b6001546001600160a01b0387811691161461405857600061405b565b60025b6000546001600160a01b0388811691161461407757600061407a565b60015b60025491019190910160ff1691506000906001600160a01b03908116908616146140a55760006140a8565b60035b6001546001600160a01b038781169116146140c45760006140c7565b60025b6000546001600160a01b038881169116146140e35760006140e6565b60015b010160ff16905081600f0b60001480614102575080600f0b6000145b1561411257600092505050611328565b600e546141299087906001600160a01b0316611e7f565b600e54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed690613fdd90600019808701919086019089906000906004016151eb565b60035460009081906001600160a01b0386811691161461418857600061418b565b60045b6002546001600160a01b038781169116146141a75760006141aa565b60035b6001546001600160a01b038881169116146141c65760006141c9565b60025b6000546001600160a01b038981169116146141e55760006141e8565b60015b01010160ff1690506000600360009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b03161461422757600061422a565b60045b6002546001600160a01b03878116911614614246576000614249565b60035b6001546001600160a01b03888116911614614265576000614268565b60025b6000546001600160a01b03898116911614614284576000614287565b60015b01010160ff16905081600f0b600014806142a4575080600f0b6000145b156142b457600092505050611328565b600f546142cb9087906001600160a01b0316611e7f565b600f54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed690613fdd90600019808701919086019089906000906004016151eb565b60045460009081906001600160a01b0386811691161461432a57600061432d565b60045b6002546001600160a01b0387811691161461434957600061434c565b60035b6001546001600160a01b0388811691161461436857600061436b565b60025b6000546001600160a01b0389811691161461438757600061438a565b60015b01010160ff1690506000600460009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b0316146143c95760006143cc565b60045b6002546001600160a01b038781169116146143e85760006143eb565b60035b6001546001600160a01b0388811691161461440757600061440a565b60025b6000546001600160a01b03898116911614614426576000614429565b60015b01010160ff16905081600f0b60001480614446575080600f0b6000145b1561445657600092505050611328565b60105461446d9087906001600160a01b0316611e7f565b601054604051635320bf6b60e11b81526001600160a01b039091169063a6417ed690613fdd90600019808701919086019089906000906004016151eb565b60055460009081906001600160a01b038681169116146144cc5760006144cf565b60055b6003546001600160a01b038781169116146144eb5760006144ee565b60045b6002546001600160a01b0388811691161461450a57600061450d565b60035b6001546001600160a01b0389811691161461452957600061452c565b60025b6000546001600160a01b038a811691161461454857600061454b565b60015b0101010160ff1690506000600560009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b03161461458b57600061458e565b60055b6003546001600160a01b038781169116146145aa5760006145ad565b60045b6002546001600160a01b038881169116146145c95760006145cc565b60035b6001546001600160a01b038981169116146145e85760006145eb565b60025b6000546001600160a01b038a811691161461460757600061460a565b60015b0101010160ff16905081600f0b60001480614628575080600f0b6000145b1561463857600092505050611328565b6005546001600160a01b0387811691161480159061466457506005546001600160a01b03868116911614155b1561467457600092505050611328565b60115461468b9087906001600160a01b0316611e7f565b601154604051635320bf6b60e11b81526001600160a01b039091169063a6417ed690613fdd90600019808701919086019089906000906004016151eb565b6000828201838110156113285760405162461bcd60e51b8152600401610ff690615232565b604051806101400160405280600a906020820280388339509192915050565b6040518061012001604052806009905b61473681526020019060019003908161471d5790505090565bfe5b805161115281615401565b600082601f83011261475457600080fd5b81516147676147628261533f565b615318565b9150818183526020840193506020810190508385602084028201111561478c57600080fd5b60005b838110156147b857816147a28882614738565b845250602092830192919091019060010161478f565b5050505092915050565b600082601f8301126147d357600080fd5b81356147e16147628261533f565b9150818183526020840193506020810190508385602084028201111561480657600080fd5b60005b838110156147b8578161481c88826148c3565b8452506020928301929190910190600101614809565b600082601f83011261484357600080fd5b81516148516147628261533f565b9150818183526020840193506020810190508385602084028201111561487657600080fd5b60005b838110156147b8578161488c88826148ce565b8452506020928301929190910190600101614879565b805161115281615418565b803561115281615421565b805161115281615421565b80356111528161542a565b80516111528161542a565b6000602082840312156148eb57600080fd5b60006119448484614738565b60006020828403121561490957600080fd5b815167ffffffffffffffff81111561492057600080fd5b61194484828501614743565b60006020828403121561493e57600080fd5b600061194484846148a2565b6000806040838503121561495d57600080fd5b600061496985856148a2565b925050602061497a858286016148ce565b9150509250929050565b60006020828403121561499657600080fd5b600061194484846148ad565b6000602082840312156149b457600080fd5b600061194484846148b8565b60008060008060008060c087890312156149d957600080fd5b60006149e589896148ad565b96505060206149f689828a016148ad565b9550506040614a0789828a016148c3565b9450506060614a1889828a016148c3565b935050608087013567ffffffffffffffff811115614a3557600080fd5b614a4189828a016147c2565b92505060a0614a5289828a016148c3565b9150509295509295509295565b600080600080600060a08688031215614a7757600080fd5b6000614a8388886148ad565b9550506020614a94888289016148ad565b9450506040614aa5888289016148c3565b9350506060614ab6888289016148c3565b9250506080614ac7888289016148c3565b9150509295509295909350565b60008060008060008060c08789031215614aed57600080fd5b6000614af989896148ad565b9650506020614b0a89828a016148ad565b9550506040614b1b89828a016148c3565b9450506060614b2c89828a016148c3565b9350506080614a4189828a016148c3565b600060208284031215614b4f57600080fd5b600061194484846148ce565b60008060408385031215614b6e57600080fd5b6000614b7a85856148ce565b925050602083015167ffffffffffffffff811115614b9757600080fd5b61497a85828601614832565b6000614baf8383614bd2565b505060200190565b6000614baf8383614fb6565b614bcc816153aa565b82525050565b614bcc81615373565b6000614be682615366565b614bf0818561536a565b9350614bfb83615360565b8060005b83811015614c29578151614c138882614ba3565b9750614c1e83615360565b925050600101614bff565b509495945050505050565b6000614c3f82615366565b614c49818561536a565b9350614c5483615360565b8060005b83811015614c29578151614c6c8882614bb7565b9750614c7783615360565b925050600101614c58565b6000614c8d82615366565b614c978185610c2d565b9350614ca78185602086016153cb565b9290920192915050565b614bcc81615383565b614bcc8161538e565b614bcc816153b5565b614bcc816153c0565b6000614ce082615366565b614cea818561536a565b9350614cfa8185602086016153cb565b614d03816153f7565b9093019392505050565b6000614d1a601b8361536a565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b6000614d5360358361536a565b7f4f6e6553706c69743a2061637475616c2072657475726e20616d6f756e74206981527439903632b9b9903a3430b71036b4b72932ba3ab93760591b602082015260400192915050565b6000614daa60208361536a565b7f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815260200192915050565b6000614de3602f8361536a565b7f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f81526e6e7461696e206e6f6e2d7a65726f7360881b602082015260400192915050565b6000614e34602b8361536a565b7f57726f6e6720757365616765206f66204554482e756e6976657273616c54726181526a6e7366657246726f6d282960a81b602082015260400192915050565b6000614e8160218361536a565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b600061115260008361536a565b6000614ed1602a8361536a565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e8152691bdd081cdd58d8d9595960b21b602082015260400192915050565b6c42616e636f724e6574776f726b60981b9052565b6000614f3260368361536a565b7f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f81527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b602082015260400192915050565b6000614f8a601f8361536a565b7f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400815260200192915050565b614bcc816153a7565b60006113288284614c82565b602081016111528284614bd2565b602081016111528284614bc3565b60408101614ff58285614bc3565b6113286020830184614bd2565b604081016150108285614bc3565b6113286020830184614fb6565b60408101614ff58285614bd2565b606081016150398286614bd2565b6150466020830185614bd2565b6119446040830184614fb6565b604081016150108285614bd2565b606080825281016150728186614bdb565b90506150816020830185614fb6565b6119446040830184614cc3565b602080825281016113288184614c34565b602081016111528284614cb1565b604081016150bb8285614cb1565b6113286020830184614cb1565b60a081016150d68288614cb1565b6150e36020830187614cb1565b6150f06040830186614fb6565b6150fd6060830185614fb6565b613c386080830184614fb6565b6101008101615119828a614cb1565b6151266020830189614fb6565b6151336040830188614cb1565b6151406060830187614bc3565b61514d6080830186614cc3565b61515a60a0830185614cc3565b61516760c0830184614bc3565b81810360e083015261517881614eb7565b9998505050505050505050565b608081016151938287614cb1565b6151a06020830186614fb6565b6151ad6040830185614cb1565b6151ba6060830184614cc3565b95945050505050565b606081016151d18286614cb1565b6151de6020830185614fb6565b6119446040830184614ccc565b608081016151f98287614cba565b6152066020830186614cba565b6151ad6040830185614fb6565b604081016150108285614cc3565b602080825281016113288184614cd5565b6020808252810161115281614d0d565b6020808252810161115281614d46565b6020808252810161115281614d9d565b6020808252810161115281614dd6565b6020808252810161115281614e27565b6020808252810161115281614e74565b6020808252810161115281614ec4565b60208101610c2d82614f10565b6020808252810161115281614f25565b6020808252810161115281614f7d565b602081016111528284614fb6565b604081016152eb8285614fb6565b81810360208301526119448184614c34565b6060810161530b8286614fb6565b6150466020830185614cc3565b60405181810167ffffffffffffffff8111828210171561533757600080fd5b604052919050565b600067ffffffffffffffff82111561535657600080fd5b5060209081020190565b60200190565b5190565b90815260200190565b60006111528261539b565b151590565b600061115282615373565b600f0b90565b61ffff1690565b6001600160a01b031690565b90565b600061115282615383565b6000611152826153a7565b600061115282615394565b60005b838110156153e65781810151838201526020016153ce565b838111156112525750506000910152565b601f01601f191690565b61540a81615373565b811461541557600080fd5b50565b61540a8161537e565b61540a81615383565b61540a816153a756fea365627a7a72315820386acf115a1df2daad0f26f1cd6cad922ffe149c2ff6f15b2eef8b7081d00ad36c6578706572696d656e74616cf564736f6c63430005110040 \ No newline at end of file diff --git a/OneSplit.full.sol b/OneSplit.full.sol index 988807b..8c3b7e8 100644 --- a/OneSplit.full.sol +++ b/OneSplit.full.sol @@ -3087,9 +3087,355 @@ contract OneSplitWeth is OneSplitBase { } } +// File: contracts/interface/ISmartTokenConverter.sol + +pragma solidity ^0.5.0;pragma experimental ABIEncoderV2; + + +interface ISmartTokenConverter { + + struct Reserve { + uint256 virtualBalance; // reserve virtual balance + uint32 ratio; // reserve ratio, represented in ppm, 1-1000000 + bool isVirtualBalanceEnabled; // true if virtual balance is enabled, false if not + bool isSaleEnabled; // is sale of the reserve token enabled, can be set by the owner + bool isSet; // used to tell if the mapping element is defined + } + + function version() external view returns (uint16); + + function reserves(address) external view returns (Reserve memory); + + function getReserveRatio(IERC20 token) external view returns (uint256); + + function connectorTokenCount() external view returns (uint256); + + function connectorTokens(uint256 i) external view returns (IERC20); +} + +// File: contracts/interface/ISmartToken.sol + +pragma solidity ^0.5.0; + + + + +interface ISmartToken { + function owner() external view returns (ISmartTokenConverter); +} + +// File: contracts/interface/ISmartTokenRegistry.sol + +pragma solidity ^0.5.0; + + + +interface ISmartTokenRegistry { + function isSmartToken(IERC20 token) external view returns (bool); +} + +// File: contracts/interface/ISmartTokenFormula.sol + +pragma solidity ^0.5.0; + + + +interface ISmartTokenFormula { + function calculateLiquidateReturn( + uint256 supply, + uint256 reserveBalance, + uint32 totalRatio, + uint256 amount + ) external view returns (uint256); + + function calculatePurchaseReturn( + uint256 supply, + uint256 reserveBalance, + uint32 totalRatio, + uint256 amount + ) external view returns (uint256); +} + +// File: contracts/OneSplitSmartToken.sol + +pragma solidity ^0.5.0; +pragma experimental ABIEncoderV2; + + + + + + + +contract OneSplitSmartTokenBase { + using SafeMath for uint256; + + ISmartTokenRegistry smartTokenRegistry = ISmartTokenRegistry(0xf6E2D7F616B67E46D708e4410746E9AAb3a4C518); + ISmartTokenFormula smartTokenFormula = ISmartTokenFormula(0x524619EB9b4cdFFa7DA13029b33f24635478AFc0); + + struct TokenWithRatio { + IERC20 token; + uint256 ratio; + } + + struct SmartTokenDetails { + TokenWithRatio[] reserveTokenList; + address converter; + uint256 totalReserveTokensRatio; + } + + function _getSmartTokenDetails(ISmartToken smartToken) internal view returns (SmartTokenDetails memory details) { + ISmartTokenConverter converter = smartToken.owner(); + (TokenWithRatio[] memory reserveTokenList, uint256 totalReserveTokensRatio) = _getTokens(converter); + + details.reserveTokenList = reserveTokenList; + details.converter = address(converter); + details.totalReserveTokensRatio = totalReserveTokensRatio; + + return details; + } + + function _getTokens( + ISmartTokenConverter converter + ) + internal + view + returns(TokenWithRatio[] memory reserveTokenList, uint256 totalRatio) + { + reserveTokenList = new TokenWithRatio[](converter.connectorTokenCount()); + for (uint256 i = 0; i < reserveTokenList.length; i++) { + reserveTokenList[i].token = converter.connectorTokens(i); + reserveTokenList[i].ratio = _getReserveRatio(converter, reserveTokenList[i].token); + totalRatio = totalRatio.add(reserveTokenList[i].ratio); + } + return (reserveTokenList, totalRatio); + } + + function _getReserveRatio( + ISmartTokenConverter converter, + IERC20 token + ) + internal + view + returns (uint256) + { + if (converter.version() >= 22) { + return converter.getReserveRatio(token); + } + + return uint256(converter.reserves(address(token)).ratio); + } + +} + + +contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { + + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + public + view + returns( + uint256, + uint256[] memory + ) + { + + if (fromToken == toToken) { + return (amount, new uint256[](9)); + } + + if (!disableFlags.check(FLAG_DISABLE_SMART_TOKEN)) { + + if (smartTokenRegistry.isSmartToken(fromToken)) { + + return _getExpectedReturnFromSmartToken( + fromToken, + toToken, + amount, + parts, + disableFlags + ); + + } + + if (smartTokenRegistry.isSmartToken(toToken)) { + + return _getExpectedReturnToSmartToken( + fromToken, + toToken, + amount, + parts, + disableFlags + ); + + } + } + + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + disableFlags + ); + } + + function _getExpectedReturnFromSmartToken( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + internal + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](9); + + SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(fromToken))); + + for (uint256 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( + fromToken.totalSupply(), + smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter), + uint32(smartTokenDetails.totalReserveTokensRatio), + amount + ); + + (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( + smartTokenDetails.reserveTokenList[i].token, + toToken, + srcAmount, + parts, + disableFlags + ); + + returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] = distribution[j].add(dist[j] << (i * 8)); + } + } + return (returnAmount, distribution); + } + + function _getExpectedReturnToSmartToken( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + internal + view + returns( + uint256 minFundAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](9); + minFundAmount = uint256(-1); + + SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(toToken))); + + uint256[] memory fundAmounts = new uint256[](smartTokenDetails.reserveTokenList.length); + for (uint i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + + uint256 exchangeAmount = _calcExchangeAmount( + amount, + smartTokenDetails.reserveTokenList[i].ratio, + smartTokenDetails.totalReserveTokensRatio + ); + + (uint256 tokenAmount, uint256[] memory dist) = super.getExpectedReturn( + fromToken, + smartTokenDetails.reserveTokenList[i].token, + exchangeAmount, + parts, + disableFlags | FLAG_DISABLE_BANCOR + ); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] = distribution[j].add(dist[j] << (i * 8)); + } + + fundAmounts[i] = toToken.totalSupply() + .mul(tokenAmount) + .div(smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter)); + + if (fundAmounts[i] < minFundAmount) { + minFundAmount = fundAmounts[i]; + } + } + + // Swap leftovers for SmartToken + for (uint i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + uint256 reserveBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter); + + uint256 leftover = fundAmounts[i].sub(minFundAmount) + .mul(reserveBalance) + .div(toToken.totalSupply()); + + if (leftover > 0) { + minFundAmount = minFundAmount.add( + smartTokenFormula.calculatePurchaseReturn( + toToken.totalSupply(), + reserveBalance, + uint32(smartTokenDetails.totalReserveTokensRatio), + leftover + ) + ); + } + } + + return (minFundAmount, distribution); + } + + function _calcExchangeAmount(uint256 amount, uint256 ratio, uint256 totalRatio) internal pure returns (uint256) { + return amount.mul(ratio).div(totalRatio); + } + +} + + +contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 disableFlags + ) internal { + if (fromToken == toToken) { + return; + } + + // TODO: + + return super._swap( + fromToken, + toToken, + amount, + distribution, + disableFlags + ); + } +} + // File: contracts/OneSplit.sol pragma solidity ^0.5.0; +pragma experimental ABIEncoderV2; + @@ -3100,7 +3446,6 @@ pragma solidity ^0.5.0; -//import "./OneSplitSmartToken.sol"; contract OneSplitView is @@ -3113,8 +3458,8 @@ contract OneSplitView is OneSplitFulcrumView, OneSplitCompoundView, OneSplitIearnView, - OneSplitWethView - //OneSplitSmartTokenView + OneSplitWethView, + OneSplitSmartTokenView { function getExpectedReturn( IERC20 fromToken, diff --git a/contracts/OneSplit.sol b/contracts/OneSplit.sol index 6a4e926..283efaa 100644 --- a/contracts/OneSplit.sol +++ b/contracts/OneSplit.sol @@ -1,4 +1,5 @@ pragma solidity ^0.5.0; +pragma experimental ABIEncoderV2; import "./IOneSplit.sol"; import "./OneSplitBase.sol"; @@ -10,7 +11,7 @@ import "./OneSplitBdai.sol"; import "./OneSplitIearn.sol"; import "./OneSplitAave.sol"; import "./OneSplitWeth.sol"; -//import "./OneSplitSmartToken.sol"; +import "./OneSplitSmartToken.sol"; contract OneSplitView is @@ -23,8 +24,8 @@ contract OneSplitView is OneSplitFulcrumView, OneSplitCompoundView, OneSplitIearnView, - OneSplitWethView - //OneSplitSmartTokenView + OneSplitWethView, + OneSplitSmartTokenView { function getExpectedReturn( IERC20 fromToken, diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 1ea899e..bee65aa 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -14,10 +14,26 @@ contract OneSplitSmartTokenBase { ISmartTokenRegistry smartTokenRegistry = ISmartTokenRegistry(0xf6E2D7F616B67E46D708e4410746E9AAb3a4C518); ISmartTokenFormula smartTokenFormula = ISmartTokenFormula(0x524619EB9b4cdFFa7DA13029b33f24635478AFc0); - struct TokensWithRatio { - IERC20[] tokens; - uint256[] ratios; - uint256 totalRatio; + struct TokenWithRatio { + IERC20 token; + uint256 ratio; + } + + struct SmartTokenDetails { + TokenWithRatio[] reserveTokenList; + address converter; + uint256 totalReserveTokensRatio; + } + + function _getSmartTokenDetails(ISmartToken smartToken) internal view returns (SmartTokenDetails memory details) { + ISmartTokenConverter converter = smartToken.owner(); + (TokenWithRatio[] memory reserveTokenList, uint256 totalReserveTokensRatio) = _getTokens(converter); + + details.reserveTokenList = reserveTokenList; + details.converter = address(converter); + details.totalReserveTokensRatio = totalReserveTokensRatio; + + return details; } function _getTokens( @@ -25,16 +41,15 @@ contract OneSplitSmartTokenBase { ) internal view - returns(TokensWithRatio memory tokens) + returns(TokenWithRatio[] memory reserveTokenList, uint256 totalRatio) { - tokens.tokens = new IERC20[](converter.connectorTokenCount()); - tokens.ratios = new uint256[](tokens.tokens.length); - for (uint256 i = 0; i < tokens.tokens.length; i++) { - tokens.tokens[i] = converter.connectorTokens(i); - tokens.ratios[i] = _getReserveRatio(converter, tokens.tokens[i]); - tokens.totalRatio = tokens.totalRatio.add(tokens.ratios[i]); + reserveTokenList = new TokenWithRatio[](converter.connectorTokenCount()); + for (uint256 i = 0; i < reserveTokenList.length; i++) { + reserveTokenList[i].token = converter.connectorTokens(i); + reserveTokenList[i].ratio = _getReserveRatio(converter, reserveTokenList[i].token); + totalRatio = totalRatio.add(reserveTokenList[i].ratio); } - return tokens; + return (reserveTokenList, totalRatio); } function _getReserveRatio( @@ -45,14 +60,13 @@ contract OneSplitSmartTokenBase { view returns (uint256) { - uint16 version = converter.version(); - - if (version >= 22) { + if (converter.version() >= 22) { return converter.getReserveRatio(token); } return uint256(converter.reserves(address(token)).ratio); } + } @@ -61,15 +75,15 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { function getExpectedReturn( IERC20 fromToken, IERC20 toToken, + uint256 amount, uint256 parts, - uint256 disableFlags, - uint256 amount + uint256 disableFlags ) public view returns( uint256, - uint256[] memory distribution + uint256[] memory ) { @@ -78,91 +92,32 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { } if (!disableFlags.check(FLAG_DISABLE_SMART_TOKEN)) { - distribution = new uint256[](9); + if (smartTokenRegistry.isSmartToken(fromToken)) { - ISmartTokenConverter converter = ISmartToken(address(fromToken)).owner(); - - TokensWithRatio memory tokens = _getTokens(converter); - - uint256 returnAmount = 0; - for (uint256 i = 0; i < tokens.tokens.length; i++) { - uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( - fromToken.totalSupply(), - tokens.tokens[i].balanceOf(address(converter)), - uint32(tokens.totalRatio), - amount - ); - - (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( - tokens.tokens[i], - toToken, - srcAmount, - parts, - disableFlags - ); - - returnAmount = returnAmount.add(ret); - for (uint j = 0; j < distribution.length; j++) { - distribution[j] = distribution[j].add(dist[j] << (i * 8)); - } - } - return (returnAmount, distribution); + + return _getExpectedReturnFromSmartToken( + fromToken, + toToken, + amount, + parts, + disableFlags + ); + } if (smartTokenRegistry.isSmartToken(toToken)) { - ISmartTokenConverter converter = ISmartToken(address(toToken)).owner(); - TokensWithRatio memory tokens = _getTokens( - converter + return _getExpectedReturnToSmartToken( + fromToken, + toToken, + amount, + parts, + disableFlags ); - uint256[] memory fundAmounts = new uint256[](tokens.tokens.length + 1); - fundAmounts[0] = uint256(-1); - for (uint256 i = 0; i < 1; i++) { - (uint256 tokenAmount, uint256[] memory dist) = super.getExpectedReturn( - fromToken, - tokens.tokens[i], - amount.mul(tokens.ratios[i]).div(tokens.totalRatio), - parts, - disableFlags | FLAG_DISABLE_BANCOR - ); - - for (uint j = 0; j < distribution.length; j++) { - distribution[j] = distribution[j].add(dist[j] << (i * 8)); - } - - fundAmounts[i + 1] = toToken.totalSupply() - .mul(tokenAmount) - .div(tokens.tokens[i].balanceOf(address(converter))); - - if (fundAmounts[i + 1] < fundAmounts[0]) { - fundAmounts[0] = fundAmounts[i + 1]; - } - } - - // Swap leftovers for SmartToken - for (uint i = 0; i < 1; i++) { - uint256 leftover = fundAmounts[i + 1].sub(fundAmounts[0]) - .mul(tokens.tokens[i].balanceOf(address(converter))) - .div(toToken.totalSupply()); - - if (leftover > 0) { - fundAmounts[0] = fundAmounts[0].add( - smartTokenFormula.calculatePurchaseReturn( - toToken.totalSupply(), - tokens.tokens[i].balanceOf(address(converter)), - uint32(tokens.totalRatio), - leftover - ) - ); - } - } - - return (fundAmounts[0], distribution); } } - return super.getExpectedReturn( fromToken, toToken, @@ -172,6 +127,124 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { ); } + function _getExpectedReturnFromSmartToken( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + internal + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](9); + + SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(fromToken))); + + for (uint256 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( + fromToken.totalSupply(), + smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter), + uint32(smartTokenDetails.totalReserveTokensRatio), + amount + ); + + (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( + smartTokenDetails.reserveTokenList[i].token, + toToken, + srcAmount, + parts, + disableFlags + ); + + returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] = distribution[j].add(dist[j] << (i * 8)); + } + } + return (returnAmount, distribution); + } + + function _getExpectedReturnToSmartToken( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + internal + view + returns( + uint256 minFundAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](9); + minFundAmount = uint256(-1); + + SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(toToken))); + + uint256[] memory fundAmounts = new uint256[](smartTokenDetails.reserveTokenList.length); + for (uint i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + + uint256 exchangeAmount = _calcExchangeAmount( + amount, + smartTokenDetails.reserveTokenList[i].ratio, + smartTokenDetails.totalReserveTokensRatio + ); + + (uint256 tokenAmount, uint256[] memory dist) = super.getExpectedReturn( + fromToken, + smartTokenDetails.reserveTokenList[i].token, + exchangeAmount, + parts, + disableFlags | FLAG_DISABLE_BANCOR + ); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] = distribution[j].add(dist[j] << (i * 8)); + } + + fundAmounts[i] = toToken.totalSupply() + .mul(tokenAmount) + .div(smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter)); + + if (fundAmounts[i] < minFundAmount) { + minFundAmount = fundAmounts[i]; + } + } + + // Swap leftovers for SmartToken + for (uint i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + uint256 reserveBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter); + + uint256 leftover = fundAmounts[i].sub(minFundAmount) + .mul(reserveBalance) + .div(toToken.totalSupply()); + + if (leftover > 0) { + minFundAmount = minFundAmount.add( + smartTokenFormula.calculatePurchaseReturn( + toToken.totalSupply(), + reserveBalance, + uint32(smartTokenDetails.totalReserveTokensRatio), + leftover + ) + ); + } + } + + return (minFundAmount, distribution); + } + + function _calcExchangeAmount(uint256 amount, uint256 ratio, uint256 totalRatio) internal pure returns (uint256) { + return amount.mul(ratio).div(totalRatio); + } + } diff --git a/contracts/interface/ISmartTokenConverter.sol b/contracts/interface/ISmartTokenConverter.sol index f2eb66f..1f6ef40 100644 --- a/contracts/interface/ISmartTokenConverter.sol +++ b/contracts/interface/ISmartTokenConverter.sol @@ -1,5 +1,4 @@ -pragma solidity ^0.5.0; -pragma experimental ABIEncoderV2; +pragma solidity ^0.5.0;pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/test/OneSplit.js b/test/OneSplit.js index ed36dc9..cdfec08 100644 --- a/test/OneSplit.js +++ b/test/OneSplit.js @@ -13,13 +13,26 @@ contract('OneSplit', function ([_, addr1]) { this.smartTokenView = await OneSplitSmartTokenView.new(); }); - it('should work', async function () { + it('should view buying price', async function () { const res = await this.smartTokenView.getExpectedReturn( '0x0000000000000000000000000000000000000000', // ETH '0x482c31355F4f7966fFcD38eC5c9635ACAe5F4D4F', // Ether Token Smart Relay Token (ETHUSDB) + '0x' + Number(web3.utils.toWei('20')).toString(16), '0x' + (10).toString(16), '0x0', - '0x' + Number(web3.utils.toWei('20')).toString(16) + ); + + console.log(res['0'].toString()); + console.log(res['1'].map(x => x.toString())); + }); + + it('should view selling price', async function () { + const res = await this.smartTokenView.getExpectedReturn( + '0x482c31355F4f7966fFcD38eC5c9635ACAe5F4D4F', // Ether Token Smart Relay Token (ETHUSDB) + '0x0000000000000000000000000000000000000000', // ETH + '0x' + Number(web3.utils.toWei('20')).toString(16), + '0x' + (10).toString(16), + '0x0' ); console.log(res['0'].toString()); From 35cb84fdf363f488c08c5e3369addef62a132e0c Mon Sep 17 00:00:00 2001 From: Kirill Date: Wed, 1 Apr 2020 00:56:30 +0300 Subject: [PATCH 04/60] add _swapFromSmartToken function --- .gitignore | 3 +- contracts/OneSplitSmartToken.sol | 93 ++++++++++++++++++-- contracts/interface/ISmartTokenConverter.sol | 5 ++ 3 files changed, 93 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 43f55ef..d7ae498 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ node_modules/ coverage/ coverage.json build/ -.vscode \ No newline at end of file +.vscode +.idea diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index bee65aa..4efbe73 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -134,7 +134,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { uint256 parts, uint256 disableFlags ) - internal + private view returns( uint256 returnAmount, @@ -145,7 +145,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(fromToken))); - for (uint256 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( fromToken.totalSupply(), smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter), @@ -176,7 +176,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { uint256 parts, uint256 disableFlags ) - internal + private view returns( uint256 minFundAmount, @@ -189,7 +189,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(toToken))); uint256[] memory fundAmounts = new uint256[](smartTokenDetails.reserveTokenList.length); - for (uint i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { uint256 exchangeAmount = _calcExchangeAmount( amount, @@ -219,7 +219,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { } // Swap leftovers for SmartToken - for (uint i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { uint256 reserveBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter); uint256 leftover = fundAmounts[i].sub(minFundAmount) @@ -241,7 +241,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { return (minFundAmount, distribution); } - function _calcExchangeAmount(uint256 amount, uint256 ratio, uint256 totalRatio) internal pure returns (uint256) { + function _calcExchangeAmount(uint256 amount, uint256 ratio, uint256 totalRatio) private pure returns (uint256) { return amount.mul(ratio).div(totalRatio); } @@ -249,6 +249,9 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { + + // todo: think about smart tokens with one token in reserve + // todo: think about the case when toToken === reserveToken function _swap( IERC20 fromToken, IERC20 toToken, @@ -260,7 +263,32 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { return; } - // TODO: + if (!disableFlags.check(FLAG_DISABLE_SMART_TOKEN)) { + + if (smartTokenRegistry.isSmartToken(fromToken)) { + + return _swapFromSmartToken( + fromToken, + toToken, + amount, + distribution, + disableFlags + ); + + } + + if (smartTokenRegistry.isSmartToken(toToken)) { + + return _swapToSmartToken( + fromToken, + toToken, + amount, + distribution, + disableFlags + ); + + } + } return super._swap( fromToken, @@ -270,4 +298,55 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { disableFlags ); } + + function _swapFromSmartToken( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 disableFlags + ) private { + + SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(fromToken))); + + uint256[] memory tokenBalanceBefore = new uint256[](smartTokenDetails.reserveTokenList.length); + uint256[][] memory dist = new uint256[][](smartTokenDetails.reserveTokenList.length); + for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + dist[i] = new uint256[](distribution.length); + + tokenBalanceBefore[i] = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + + for (uint j = 0; j < distribution.length; j++) { + dist[i][j] = (distribution[j] >> (i * 8)) & 0xFF; + } + } + + ISmartTokenConverter(smartTokenDetails.converter).liquidate(amount); + + for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + + return super._swap( + smartTokenDetails.reserveTokenList[i].token, + toToken, + tokenBalanceAfter.sub(tokenBalanceBefore[i]), + dist[i], + disableFlags + ); + } + + } + + function _swapToSmartToken( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 disableFlags + ) private { + +// _infiniteApproveIfNeeded(smartTokenDetails.reserveTokenList[i].token, smartTokenDetails.converter); + + } + } diff --git a/contracts/interface/ISmartTokenConverter.sol b/contracts/interface/ISmartTokenConverter.sol index 1f6ef40..4aafe87 100644 --- a/contracts/interface/ISmartTokenConverter.sol +++ b/contracts/interface/ISmartTokenConverter.sol @@ -21,4 +21,9 @@ interface ISmartTokenConverter { function connectorTokenCount() external view returns (uint256); function connectorTokens(uint256 i) external view returns (IERC20); + + function liquidate(uint256 _amount) external; + + function fund(uint256 _amount) external; + } From 3350e6e22264bf8645f41710e83b13a0a15c14d8 Mon Sep 17 00:00:00 2001 From: Kirill Date: Wed, 1 Apr 2020 01:52:54 +0300 Subject: [PATCH 05/60] add _swapToSmartToken function --- OneSplit.full.sol | 188 +++++++++++++++++-- contracts/OneSplitSmartToken.sol | 92 ++++++++- contracts/interface/ISmartTokenConverter.sol | 4 + 3 files changed, 266 insertions(+), 18 deletions(-) diff --git a/OneSplit.full.sol b/OneSplit.full.sol index 8c3b7e8..518583a 100644 --- a/OneSplit.full.sol +++ b/OneSplit.full.sol @@ -3111,6 +3111,15 @@ interface ISmartTokenConverter { function connectorTokenCount() external view returns (uint256); function connectorTokens(uint256 i) external view returns (IERC20); + + function liquidate(uint256 _amount) external; + + function fund(uint256 _amount) external; + + function convert2(IERC20 _fromToken, IERC20 _toToken, uint256 _amount, uint256 _minReturn, address _affiliateAccount, uint256 _affiliateFee) external returns (uint256); + + function convert(IERC20 _fromToken, IERC20 _toToken, uint256 _amount, uint256 _minReturn) external returns (uint256); + } // File: contracts/interface/ISmartToken.sol @@ -3166,7 +3175,6 @@ pragma experimental ABIEncoderV2; - contract OneSplitSmartTokenBase { using SafeMath for uint256; @@ -3226,6 +3234,10 @@ contract OneSplitSmartTokenBase { return uint256(converter.reserves(address(token)).ratio); } + function _calcExchangeAmount(uint256 amount, uint256 ratio, uint256 totalRatio) internal pure returns (uint256) { + return amount.mul(ratio).div(totalRatio); + } + } @@ -3293,7 +3305,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { uint256 parts, uint256 disableFlags ) - internal + private view returns( uint256 returnAmount, @@ -3304,7 +3316,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(fromToken))); - for (uint256 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( fromToken.totalSupply(), smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter), @@ -3335,7 +3347,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { uint256 parts, uint256 disableFlags ) - internal + private view returns( uint256 minFundAmount, @@ -3348,7 +3360,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(toToken))); uint256[] memory fundAmounts = new uint256[](smartTokenDetails.reserveTokenList.length); - for (uint i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { uint256 exchangeAmount = _calcExchangeAmount( amount, @@ -3378,7 +3390,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { } // Swap leftovers for SmartToken - for (uint i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { uint256 reserveBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter); uint256 leftover = fundAmounts[i].sub(minFundAmount) @@ -3400,14 +3412,15 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { return (minFundAmount, distribution); } - function _calcExchangeAmount(uint256 amount, uint256 ratio, uint256 totalRatio) internal pure returns (uint256) { - return amount.mul(ratio).div(totalRatio); - } - } contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { + + // todo: think about smart tokens with one token in reserve + // todo: think about the case when toToken == reserveToken + // todo: think about the case when fromToken == reserveToken + // todo: think about the case when fromToken == smartToken1, toToken == smartToken2 function _swap( IERC20 fromToken, IERC20 toToken, @@ -3419,7 +3432,32 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { return; } - // TODO: + if (!disableFlags.check(FLAG_DISABLE_SMART_TOKEN)) { + + if (smartTokenRegistry.isSmartToken(fromToken)) { + + return _swapFromSmartToken( + fromToken, + toToken, + amount, + distribution, + disableFlags + ); + + } + + if (smartTokenRegistry.isSmartToken(toToken)) { + + return _swapToSmartToken( + fromToken, + toToken, + amount, + distribution, + disableFlags + ); + + } + } return super._swap( fromToken, @@ -3429,6 +3467,134 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { disableFlags ); } + + function _swapFromSmartToken( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 disableFlags + ) private { + + SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(fromToken))); + + uint256[] memory tokenBalanceBefore = new uint256[](smartTokenDetails.reserveTokenList.length); + uint256[][] memory dist = new uint256[][](smartTokenDetails.reserveTokenList.length); + for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + dist[i] = new uint256[](distribution.length); + + tokenBalanceBefore[i] = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + + for (uint j = 0; j < distribution.length; j++) { + dist[i][j] = (distribution[j] >> (i * 8)) & 0xFF; + } + } + + ISmartTokenConverter(smartTokenDetails.converter).liquidate(amount); + + for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + + return super._swap( + smartTokenDetails.reserveTokenList[i].token, + toToken, + tokenBalanceAfter.sub(tokenBalanceBefore[i]), + dist[i], + disableFlags + ); + } + + } + + function _swapToSmartToken( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 disableFlags + ) private { + + uint256 minFundAmount = uint256(-1); + + SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(toToken))); + + uint256[] memory fundAmounts = new uint256[](smartTokenDetails.reserveTokenList.length); + for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + + uint256 exchangeAmount = _calcExchangeAmount( + amount, + smartTokenDetails.reserveTokenList[i].ratio, + smartTokenDetails.totalReserveTokensRatio + ); + + uint256 tokenBalanceBefore = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + super._swap( + fromToken, + smartTokenDetails.reserveTokenList[i].token, + exchangeAmount, + distribution, + disableFlags + ); + + uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + + fundAmounts[i] = toToken.totalSupply() + .mul(tokenBalanceAfter.sub(tokenBalanceBefore)) + .div(smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter)); + + if (fundAmounts[i] < minFundAmount) { + minFundAmount = fundAmounts[i]; + } + + _infiniteApproveIfNeeded(smartTokenDetails.reserveTokenList[i].token, smartTokenDetails.converter); + } + + ISmartTokenConverter(smartTokenDetails.converter).fund(minFundAmount); + + // Swap leftovers for SmartToken + for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + uint256 reserveBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter); + + uint256 leftover = fundAmounts[i].sub(minFundAmount) + .mul(reserveBalance) + .div(toToken.totalSupply()); + + if (leftover > 0) { + + convert( + ISmartTokenConverter(smartTokenDetails.converter), + smartTokenDetails.reserveTokenList[i].token, + toToken, + leftover + ); + + } + } + + } + + function convert( + ISmartTokenConverter converter, + IERC20 _fromToken, + IERC20 _toToken, + uint256 _amount + ) + private + returns (uint256) + { + // todo: think about minReturn, affiliateAccount, affiliateFee + if (converter.version() >= 16) { + return converter.convert2(_fromToken, _toToken, _amount, 0, address(0), 0); + } + + return converter.convert(_fromToken, _toToken, _amount, 0); + } + } // File: contracts/OneSplit.sol diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 4efbe73..c93af38 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -7,7 +7,6 @@ import "./interface/ISmartTokenConverter.sol"; import "./interface/ISmartTokenFormula.sol"; import "./OneSplitBase.sol"; - contract OneSplitSmartTokenBase { using SafeMath for uint256; @@ -67,6 +66,10 @@ contract OneSplitSmartTokenBase { return uint256(converter.reserves(address(token)).ratio); } + function _calcExchangeAmount(uint256 amount, uint256 ratio, uint256 totalRatio) internal pure returns (uint256) { + return amount.mul(ratio).div(totalRatio); + } + } @@ -241,17 +244,15 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { return (minFundAmount, distribution); } - function _calcExchangeAmount(uint256 amount, uint256 ratio, uint256 totalRatio) private pure returns (uint256) { - return amount.mul(ratio).div(totalRatio); - } - } contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { // todo: think about smart tokens with one token in reserve - // todo: think about the case when toToken === reserveToken + // todo: think about the case when toToken == reserveToken + // todo: think about the case when fromToken == reserveToken + // todo: think about the case when fromToken == smartToken1, toToken == smartToken2 function _swap( IERC20 fromToken, IERC20 toToken, @@ -345,8 +346,85 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { uint256 disableFlags ) private { -// _infiniteApproveIfNeeded(smartTokenDetails.reserveTokenList[i].token, smartTokenDetails.converter); + uint256 minFundAmount = uint256(-1); + + SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(toToken))); + + uint256[] memory fundAmounts = new uint256[](smartTokenDetails.reserveTokenList.length); + for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + + uint256 exchangeAmount = _calcExchangeAmount( + amount, + smartTokenDetails.reserveTokenList[i].ratio, + smartTokenDetails.totalReserveTokensRatio + ); + + uint256 tokenBalanceBefore = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + super._swap( + fromToken, + smartTokenDetails.reserveTokenList[i].token, + exchangeAmount, + distribution, + disableFlags + ); + + uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + + fundAmounts[i] = toToken.totalSupply() + .mul(tokenBalanceAfter.sub(tokenBalanceBefore)) + .div(smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter)); + + if (fundAmounts[i] < minFundAmount) { + minFundAmount = fundAmounts[i]; + } + + _infiniteApproveIfNeeded(smartTokenDetails.reserveTokenList[i].token, smartTokenDetails.converter); + } + + ISmartTokenConverter(smartTokenDetails.converter).fund(minFundAmount); + + // Swap leftovers for SmartToken + for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + uint256 reserveBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter); + + uint256 leftover = fundAmounts[i].sub(minFundAmount) + .mul(reserveBalance) + .div(toToken.totalSupply()); + + if (leftover > 0) { + + convert( + ISmartTokenConverter(smartTokenDetails.converter), + smartTokenDetails.reserveTokenList[i].token, + toToken, + leftover + ); + + } + } + + } + + function convert( + ISmartTokenConverter converter, + IERC20 _fromToken, + IERC20 _toToken, + uint256 _amount + ) + private + returns (uint256) + { + // todo: think about minReturn, affiliateAccount, affiliateFee + if (converter.version() >= 16) { + return converter.convert2(_fromToken, _toToken, _amount, 0, address(0), 0); + } + return converter.convert(_fromToken, _toToken, _amount, 0); } } diff --git a/contracts/interface/ISmartTokenConverter.sol b/contracts/interface/ISmartTokenConverter.sol index 4aafe87..8478411 100644 --- a/contracts/interface/ISmartTokenConverter.sol +++ b/contracts/interface/ISmartTokenConverter.sol @@ -26,4 +26,8 @@ interface ISmartTokenConverter { function fund(uint256 _amount) external; + function convert2(IERC20 _fromToken, IERC20 _toToken, uint256 _amount, uint256 _minReturn, address _affiliateAccount, uint256 _affiliateFee) external returns (uint256); + + function convert(IERC20 _fromToken, IERC20 _toToken, uint256 _amount, uint256 _minReturn) external returns (uint256); + } From dc2a84c1d2b7762572fe3ca4fc4b5ae114250c5c Mon Sep 17 00:00:00 2001 From: Kirill Date: Wed, 1 Apr 2020 15:35:56 +0300 Subject: [PATCH 06/60] update full contract --- OneSplit.full.bin | 2 +- OneSplit.full.sol | 11 ++++++----- contracts/OneSplit.sol | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/OneSplit.full.bin b/OneSplit.full.bin index f5d9a61..d89f14b 100644 --- a/OneSplit.full.bin +++ b/OneSplit.full.bin @@ -1 +1 @@ -6080604052600080546001600160a01b0319908116736b175474e89094c44da98b954eedeac495271d0f1790915560018054821673a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4817905560028054821673dac17f958d2ee523a2206206994597c13d831ec71790556003805482166e085d4780b73119b644ae5ecd22b376179055600480548216734fabb145d64652a948d72533023f6e7a623c7c531790556005805482167357ab1ec28d129707052df4df418d58a2d46d5f5117905560068054821673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc217905560078054821673c0829421c1d260bd3cb3e0f06cfe2d52db2ce31517905560088054821673818e6fecd516ecc3849daf6845e3ec868087b75517905560098054821673c0a47dfe034b400b47bdad5fecda2621de6c4d95179055600a805482167352ae12abe5d8bd778bd5397f99ca900624cfadd4179055600b80548216736f0cd8c4f6f06eab664c7e3031909452b4b72861179055600c8054821673794e6e91555438afc3ccf1c5076a74f42133d08d179055600d8054821673a2b47e3d5c44877cca798226b7b8118f9bfb7a56179055600e805482167352ea46506b9cc5ef470c5bf89f17dc28bb35d85c179055600f805482167345f783cce6b7ff23b2ab2d70e416cdb7d6055f511790556010805482167379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27179055601180548216733b12e1fbb468bea80b492d635976809bf950186c1790556012805482167306af07097c9eeb7fd685c692751d5c66db49c215179055601380548216736a4ffaafa8dd400676df8076ad6c724867b0e2e817905560148054821673b683d83a532e2cb7dfa5275eed3698436371cc9f17905560158054821673398ec7346dcd622edc5ae82352f02be94c62d119179055601680548216733d9819210a31b4961b30ef54be2aed79b9c9cd3b17905560178054909116734ddc2d193948926d02f9b1fe9e1daa0718270ed5179055348015620002e857600080fd5b5060405162005833380380620058338339810160408190526200030b9162000344565b601880546001600160a01b0319166001600160a01b0392909216919091179055620003ad565b80516200033e8162000393565b92915050565b6000602082840312156200035757600080fd5b600062000365848462000331565b949350505050565b60006200033e8262000387565b60006200033e826200036d565b6001600160a01b031690565b6200039e816200037a565b8114620003aa57600080fd5b50565b61547680620003bd6000396000f3fe6080604052600436106103355760003560e01c806372b6f1bf116101ab578063c762a46c116100f7578063dc1536b211610095578063f4b9fa751161006f578063f4b9fa75146107c4578063f56e281f146107d9578063f69e2046146107ee578063fbe4ed951461080357610335565b8063dc1536b21461076f578063e2a7515e14610784578063f484966b1461079757610335565b8063c9b42c67116100d1578063c9b42c671461071b578063cede5f6a14610730578063d393c3e914610745578063d77366a41461075a57610335565b8063c762a46c146106dc578063c77b9de6146106f1578063c92577751461070657610335565b80638bdb2afa11610164578063b0a7ef291161013e578063b0a7ef2914610688578063b3bc78441461069d578063b69d0456146106b2578063c11f4f11146106c757610335565b80638bdb2afa14610649578063a1b4d0111461065e578063a734f06e1461067357610335565b806372b6f1bf146105c057806375a8b012146105e057806375b5be2d146105f55780637a88bdbd1461060a578063819faf7b1461061f578063851954fa1461063457610335565b80633e413bee116102855780635aa8fb481161022357806364ec4e5c116101fd57806364ec4e5c1461056e5780636b5a4ca2146105835780636b9589aa146105985780636cbc4a6e146105ab57610335565b80635aa8fb481461052f5780635ae51b82146105445780635c0cb4791461055957610335565b806344211d621161025f57806344211d62146104db5780634a7101d5146104f05780634b57b0be1461050557806351f1985c1461051a57610335565b80633e413bee146104915780634037f967146104a6578063423d03f9146104c657610335565b806322320c98116102f25780632f48ab7d116102cc5780632f48ab7d1461043d57806334b4dabb14610452578063372a26cb146104675780633ca5b2341461047c57610335565b806322320c98146103fe5780632d3b5207146104135780632e707bd21461042857610335565b8063085e2c5b1461034457806312dea1601461037b5780631388b4201461039d57806313989140146103b25780632113240d146103d457806321a360f5146103e9575b3332141561034257600080fd5b005b34801561035057600080fd5b5061036461035f366004614a5f565b610818565b6040516103729291906152dd565b60405180910390f35b34801561038757600080fd5b506103906108b8565b604051610372919061509f565b3480156103a957600080fd5b506103906108c7565b3480156103be57600080fd5b506103c76108d6565b60405161037291906152cf565b3480156103e057600080fd5b506103c76108dc565b3480156103f557600080fd5b506103c76108e2565b34801561040a57600080fd5b506103906108eb565b34801561041f57600080fd5b506103c76108fa565b34801561043457600080fd5b506103c7610903565b34801561044957600080fd5b50610390610908565b34801561045e57600080fd5b506103c7610917565b34801561047357600080fd5b5061039061091c565b34801561048857600080fd5b5061039061092b565b34801561049d57600080fd5b5061039061093a565b3480156104b257600080fd5b506103906104c1366004614984565b610949565b3480156104d257600080fd5b50610390610c32565b3480156104e757600080fd5b506103c7610c41565b3480156104fc57600080fd5b506103c7610c46565b34801561051157600080fd5b50610390610c4b565b34801561052657600080fd5b50610390610c5a565b34801561053b57600080fd5b506103c7610c69565b34801561055057600080fd5b506103c7610c6f565b34801561056557600080fd5b506103c7610c75565b34801561057a57600080fd5b506103c7610c7a565b34801561058f57600080fd5b50610390610c81565b6103426105a6366004614ad4565b610c90565b3480156105b757600080fd5b506103c7610cb9565b3480156105cc57600080fd5b506103906105db366004614984565b610cc0565b3480156105ec57600080fd5b506103c7610eaa565b34801561060157600080fd5b50610390610eb0565b34801561061657600080fd5b506103c7610ebf565b34801561062b57600080fd5b50610390610ec4565b34801561064057600080fd5b50610390610ed3565b34801561065557600080fd5b50610390610ee2565b34801561066a57600080fd5b50610390610ef1565b34801561067f57600080fd5b50610390610f00565b34801561069457600080fd5b506103c7610f18565b3480156106a957600080fd5b506103c7610f1e565b3480156106be57600080fd5b50610390610f27565b3480156106d357600080fd5b50610390610f36565b3480156106e857600080fd5b506103c7610f45565b3480156106fd57600080fd5b506103c7610f4a565b34801561071257600080fd5b50610390610f50565b34801561072757600080fd5b506103c7610f5f565b34801561073c57600080fd5b50610390610f66565b34801561075157600080fd5b506103c7610f75565b34801561076657600080fd5b50610390610f7c565b34801561077b57600080fd5b506103c7610f8b565b6103426107923660046149c0565b610f91565b3480156107a357600080fd5b506107b76107b2366004614a5f565b611058565b604051610372919061508e565b3480156107d057600080fd5b506103906110ec565b3480156107e557600080fd5b506103c76110fb565b3480156107fa57600080fd5b50610390611100565b34801561080f57600080fd5b5061039061110f565b60185460405163085e2c5b60e01b81526000916060916001600160a01b039091169063085e2c5b90610856908a908a908a908a908a906004016150c8565b60006040518083038186803b15801561086e57600080fd5b505afa158015610882573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526108aa9190810190614b5b565b915091509550959350505050565b600a546001600160a01b031681565b600c546001600160a01b031681565b61200081565b61800081565b64020000000081565b6011546001600160a01b031681565b64010000000081565b608081565b6002546001600160a01b031681565b604081565b6010546001600160a01b031681565b6004546001600160a01b031681565b6001546001600160a01b031681565b600061095d826001600160a01b031661111e565b1561096b5750600019610c2d565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b17905290516000916060916001600160a01b03861691611388916109b49190614fbf565b6000604051808303818686fa925050503d80600081146109f0576040519150601f19603f3d011682016040523d82523d6000602084013e6109f5565b606091505b509150915081610a0b5760001992505050610c2d565b6000805b6007835103811015610b6457828160000181518110610a2a57fe5b6020910101516001600160f81b031916602360f91b148015610a6d5750828160010181518110610a5657fe5b6020910101516001600160f81b031916607560f81b145b8015610a9a5750828160020181518110610a8357fe5b6020910101516001600160f81b031916601b60fa1b145b8015610ac75750828160030181518110610ab057fe5b6020910101516001600160f81b031916606360f81b145b8015610af45750828160040181518110610add57fe5b6020910101516001600160f81b031916603960f91b145b8015610b215750828160050181518110610b0a57fe5b6020910101516001600160f81b031916607560f81b145b8015610b4e5750828160060181518110610b3757fe5b6020910101516001600160f81b031916606d60f81b145b15610b5c5760019150610b64565b600101610a0f565b5080610b77576000199350505050610c2d565b60408051600481526024810182526020810180516001600160e01b031663797bf38560e01b17905290516001600160a01b0387169161138891610bba9190614fbf565b6000604051808303818686fa925050503d8060008114610bf6576040519150601f19603f3d011682016040523d82523d6000602084013e610bfb565b606091505b50909350915082610c13576000199350505050610c2d565b81806020019051610c2791908101906149a2565b93505050505b919050565b600f546001600160a01b031681565b601081565b602081565b6006546001600160a01b031681565b600d546001600160a01b031681565b61400081565b61080081565b600881565b6202000081565b6014546001600160a01b031681565b6060610c9f8787878686610818565b915050610cb0878787878587610f91565b50505050505050565b6208000081565b6000610cd4826001600160a01b031661111e565b15610ce25750600019610c2d565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b17905290516000916060916001600160a01b0386169161138891610d2b9190614fbf565b6000604051808303818686fa925050503d8060008114610d67576040519150601f19603f3d011682016040523d82523d6000602084013e610d6c565b606091505b509150915081610d825760001992505050610c2d565b6000805b6004835103811015610e5457828160000181518110610da157fe5b6020910101516001600160f81b031916604160f81b148015610de45750828160010181518110610dcd57fe5b6020910101516001600160f81b031916606160f81b145b8015610e115750828160020181518110610dfa57fe5b6020910101516001600160f81b031916603b60f91b145b8015610e3e5750828160030181518110610e2757fe5b6020910101516001600160f81b031916606560f81b145b15610e4c5760019150610e54565b600101610d86565b5080610e67576000199350505050610c2d565b60408051600481526024810182526020810180516001600160e01b0316632274683f60e21b17905290516001600160a01b0387169161138891610bba9190614fbf565b61040081565b6003546001600160a01b031681565b600281565b6015546001600160a01b031681565b6007546001600160a01b031681565b6009546001600160a01b031681565b6017546001600160a01b031681565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b6012546001600160a01b031681565b6013546001600160a01b031681565b600181565b61020081565b6005546001600160a01b031681565b6204000081565b600e546001600160a01b031681565b6201000081565b6008546001600160a01b031681565b61010081565b610fac6001600160a01b03871633308763ffffffff61115816565b610fb98686868585611258565b6000610fd46001600160a01b0387163063ffffffff61128b16565b905083811015610fff5760405162461bcd60e51b8152600401610ff690615242565b60405180910390fd5b6110196001600160a01b038716338363ffffffff61132f16565b5061104e336110376001600160a01b038a163063ffffffff61128b16565b6001600160a01b038a16919063ffffffff61132f16565b5050505050505050565b606082604051908082528060200260200182016040528015611084578160200160208202803883390190505b50905060005b838110156110e2576110c287876110ba876110ae8a6001880163ffffffff6113ac16565b9063ffffffff6113e616565b600187610818565b508282815181106110cf57fe5b602090810291909101015260010161108a565b5095945050505050565b6000546001600160a01b031681565b600481565b6016546001600160a01b031681565b6018546001600160a01b031681565b60006001600160a01b038216158061115257506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b92915050565b8061116257611252565b61116b8461111e565b15611237576001600160a01b038316331480156111885750803410155b6111a45760405162461bcd60e51b8152600401610ff690615272565b6001600160a01b03821630146111ec576040516001600160a01b0383169082156108fc029083906000818181858888f193505050501580156111ea573d6000803e3d6000fd5b505b8034111561123257336108fc611208348463ffffffff61142816565b6040518115909202916000818181858888f19350505050158015611230573d6000803e3d6000fd5b505b611252565b6112526001600160a01b03851684848463ffffffff61146a16565b50505050565b836001600160a01b0316856001600160a01b0316141561127757611284565b61128485858585856114c5565b5050505050565b60006112968361111e565b156112ac57506001600160a01b03811631611152565b6040516370a0823160e01b81526001600160a01b038416906370a08231906112d8908590600401614fcb565b60206040518083038186803b1580156112f057600080fd5b505afa158015611304573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506113289190810190614b3d565b9392505050565b60008161133e57506001611328565b6113478461111e565b15611388576040516001600160a01b0384169083156108fc029084906000818181858888f19350505050158015611382573d6000803e3d6000fd5b50611328565b6113a26001600160a01b038516848463ffffffff6114d216565b5060019392505050565b6000826113bb57506000611152565b828202828482816113c857fe5b04146113285760405162461bcd60e51b8152600401610ff690615282565b600061132883836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506114f9565b600061132883836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250611530565b6040516112529085906323b872dd60e01b9061148e9087908790879060240161502b565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261155c565b6112848585858585611641565b6040516114f490849063a9059cbb60e01b9061148e9086908690602401615053565b505050565b6000818361151a5760405162461bcd60e51b8152600401610ff69190615221565b50600083858161152657fe5b0495945050505050565b600081848411156115545760405162461bcd60e51b8152600401610ff69190615221565b505050900390565b61156e826001600160a01b0316611910565b61158a5760405162461bcd60e51b8152600401610ff6906152bf565b60006060836001600160a01b0316836040516115a69190614fbf565b6000604051808303816000865af19150503d80600081146115e3576040519150601f19603f3d011682016040523d82523d6000602084013e6115e8565b606091505b50915091508161160a5760405162461bcd60e51b8152600401610ff690615252565b8051156112525780806020019051611625919081019061492c565b6112525760405162461bcd60e51b8152600401610ff690615292565b836001600160a01b0316856001600160a01b0316141561166057611284565b611673816208000063ffffffff61194c16565b611903576006546001600160a01b0386811691161415611789576006546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a08231906116c5903090600401614fd9565b60206040518083038186803b1580156116dd57600080fd5b505afa1580156116f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117159190810190614b3d565b6040518263ffffffff1660e01b815260040161173191906152cf565b600060405180830381600087803b15801561174b57600080fd5b505af115801561175f573d6000803e3d6000fd5b5050505061178473eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee85858585611952565b611284565b6007546001600160a01b03868116911614156117d7576007546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a08231906116c5903090600401614fd9565b6006546001600160a01b038581169116141561187c5761180e8573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585611641565b600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b15801561185e57600080fd5b505af1158015611872573d6000803e3d6000fd5b5050505050611284565b6007546001600160a01b0385811691161415611903576118b38573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585611641565b600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b15801561185e57600080fd5b6112848585858585611952565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061194457508115155b949350505050565b16151590565b61128485858585855b836001600160a01b0316856001600160a01b0316141561197a57611284565b6119826146ee565b61198a611d56565b905061199e8261080063ffffffff61194c16565b611d415760005b600a811015611b63578181600a81106119ba57fe5b60200201516001600160a01b0316876001600160a01b03161415611b5b5760008282600a81106119e657fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015611a2357600080fd5b505afa158015611a37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a5b91908101906149a2565b90508282600a8110611a6957fe5b60200201516001600160a01b0316632e1a7d4d876040518263ffffffff1660e01b8152600401611a9991906152cf565b600060405180830381600087803b158015611ab357600080fd5b505af1158015611ac7573d6000803e3d6000fd5b50505050611b538188836001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611afc9190614fd9565b60206040518083038186803b158015611b1457600080fd5b505afa158015611b28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b4c9190810190614b3d565b888861195b565b505050611284565b6001016119a5565b5060005b600a811015611d3f578181600a8110611b7c57fe5b60200201516001600160a01b0316866001600160a01b03161415611d375760008282600a8110611ba857fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015611be557600080fd5b505afa158015611bf9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c1d91908101906149a2565b9050611c2c8882888888611e72565b611c46818484600a8110611c3c57fe5b6020020151611e7f565b8282600a8110611c5257fe5b60200201516001600160a01b031663b6b55f25826001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611c919190614fd9565b60206040518083038186803b158015611ca957600080fd5b505afa158015611cbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ce19190810190614b3d565b6040518263ffffffff1660e01b8152600401611cfd91906152cf565b600060405180830381600087803b158015611d1757600080fd5b505af1158015611d2b573d6000803e3d6000fd5b50505050505050611284565b600101611b67565b505b611d4e8686868686611e72565b505050505050565b611d5e6146ee565b5060408051610140810182527316de59092dae5ccf4a1e6439d611fd0653f0bd0181527304aa51bbcb46541455ccf1b8bef2ebc5d3787ec960208201527373a052500105205d34daf004eab301916da8190f918101919091527383f798e925bcd4017eb265844fddabb448f1707d606082015273d6ad7a6750a7593e092a9b218d66c0a814a3436e608082015273f61718057901f84c4eec4339ef8f0d86d2b4560060a08201527304bc0ab673d88ae9dbc9da2380cb6b79c4bca9ae60c082015273c2cb1040220768554cf699b0d863a3cd4324ce3260e082015273e6354ed5bc4b393a5aad09f21c46e101e692d4476101008201527326ea744e5b887e5205727f55dfbe8685e3b2195161012082015290565b6112848585858585611f3c565b611e91826001600160a01b031661111e565b611f3857604051636eb1769f60e11b815260ff906001600160a01b0384169063dd62ed3e90611ec69030908690600401614fe7565b60206040518083038186803b158015611ede57600080fd5b505afa158015611ef2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f169190810190614b3d565b901c611f3857611f386001600160a01b0383168260001963ffffffff61219b16565b5050565b836001600160a01b0316856001600160a01b03161415611f5b57611284565b611f6c81601063ffffffff61194c16565b61218e57611f7985612270565b1561203c576000611f898661236e565b60405163db006a7560e01b81529091506001600160a01b0387169063db006a7590611fb89087906004016152cf565b602060405180830381600087803b158015611fd257600080fd5b505af1158015611fe6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061200a9190810190614b3d565b5060006120266001600160a01b0383163063ffffffff61128b16565b90506120358287838787611f3c565b5050611284565b61204584612270565b1561218e5760006120558561236e565b90506120648682868686612443565b600061207f6001600160a01b0383163063ffffffff61128b16565b9050612093826001600160a01b031661111e565b1561210657601760009054906101000a90046001600160a01b03166001600160a01b0316631249c58b826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156120e857600080fd5b505af11580156120fc573d6000803e3d6000fd5b5050505050612035565b6121108287611e7f565b60405163140e25ad60e31b81526001600160a01b0387169063a0712d689061213c9084906004016152cf565b602060405180830381600087803b15801561215657600080fd5b505af115801561216a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611b539190810190614b3d565b6112848585858585612443565b6121a48361111e565b6114f4576000811180156122365750604051636eb1769f60e11b81526000906001600160a01b0385169063dd62ed3e906121e4903090879060040161501d565b60206040518083038186803b1580156121fc57600080fd5b505afa158015612210573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506122349190810190614b3d565b115b15612256576122566001600160a01b03841683600063ffffffff61245016565b6114f46001600160a01b038416838363ffffffff61245016565b6017546000906001600160a01b038381169116141561229157506001610c2d565b6016546040516000916060916001600160a01b039091169061138890638e8f294b60e01b906122c490889060240161509f565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516123029190614fbf565b6000604051808303818686fa925050503d806000811461233e576040519150601f19603f3d011682016040523d82523d6000602084013e612343565b606091505b50915091508161235857600092505050610c2d565b6000818060200190516110e2919081019061494a565b6017546000906001600160a01b038381169116141561238f57506000610c2d565b60408051600481526024810182526020810180516001600160e01b0316636f307dc360e01b17905290516000916060916001600160a01b03861691611388916123d89190614fbf565b6000604051808303818686fa925050503d8060008114612414576040519150601f19603f3d011682016040523d82523d6000602084013e612419565b606091505b50915091508161242f5760001992505050610c2d565b8080602001905161194491908101906149a2565b6112848585858585612516565b8015806124d85750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e90612486903090869060040161501d565b60206040518083038186803b15801561249e57600080fd5b505afa1580156124b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506124d69190810190614b3d565b155b6124f45760405162461bcd60e51b8152600401610ff6906152af565b6040516114f490849063095ea7b360e01b9061148e9086908690602401615053565b836001600160a01b0316856001600160a01b0316141561253557611284565b61254681602063ffffffff61194c16565b6127cc57600061255586610949565b90506001600160a01b03808216146126b057612579816001600160a01b031661111e565b156126045760405163081a6b2560e41b81526001600160a01b038716906381a6b250906125ac9030908890600401615002565b602060405180830381600087803b1580156125c657600080fd5b505af11580156125da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506125fe9190810190614b3d565b50612686565b604051632770a7eb60e21b81526001600160a01b03871690639dc29fac906126329030908890600401615002565b602060405180830381600087803b15801561264c57600080fd5b505af1158015612660573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506126849190810190614b3d565b505b60006126a16001600160a01b0383163063ffffffff61128b16565b905061203582878387876127d5565b6126b985610949565b90506001600160a01b03808216146127ca576126d886828686866127d5565b60006126f36001600160a01b0383163063ffffffff61128b16565b9050612707826001600160a01b031661111e565b1561279257604051638f6ede1f60e01b81526001600160a01b03871690638f6ede1f90839061273a903090600401614fd9565b6020604051808303818588803b15801561275357600080fd5b505af1158015612767573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525061278c9190810190614b3d565b50612035565b61279c8287611e7f565b6040516340c10f1960e01b81526001600160a01b038716906340c10f199061213c9030908590600401615002565b505b61128485858585855b61128485858585855b836001600160a01b0316856001600160a01b031614156127fd57611284565b61280e81608063ffffffff61194c16565b612a1057600061281d86610cc0565b90506001600160a01b03808216146128a05760405163db006a7560e01b81526001600160a01b0387169063db006a759061285b9087906004016152cf565b600060405180830381600087803b15801561287557600080fd5b505af1158015612889573d6000803e3d6000fd5b5050505061289a81868686866127de565b50611284565b6128a985610cc0565b90506001600160a01b0380821614612a0e576128c88682868686612a19565b60006128e36001600160a01b0383163063ffffffff61128b16565b905061297482601560009054906101000a90046001600160a01b03166001600160a01b031663f2f4eb266040518163ffffffff1660e01b815260040160206040518083038186803b15801561293757600080fd5b505afa15801561294b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061296f91908101906148d9565b611e7f565b6015546001600160a01b039081169063d2d0e0669061299490851661111e565b61299f5760006129a1565b825b6129b3856001600160a01b031661111e565b6129bd57846129d3565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b8461044d6040518563ffffffff1660e01b81526004016129f5939291906151c3565b6000604051808303818588803b158015611d1757600080fd5b505b61128485858585855b836001600160a01b0316856001600160a01b03161415612a3857611284565b612a4a8161040063ffffffff61194c16565b612cd0576013546001600160a01b0386811691161415612ba657601354604051637f8661a160e01b81526001600160a01b0390911690637f8661a190612a949086906004016152cf565b600060405180830381600087803b158015612aae57600080fd5b505af1158015612ac2573d6000803e3d6000fd5b50506014546040516370a0823160e01b8152600093506001600160a01b0390911691506370a0823190612af9903090600401614fd9565b60206040518083038186803b158015612b1157600080fd5b505afa158015612b25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b499190810190614b3d565b90508015612b8d57601454606090612b6e906001600160a01b03168784600187610818565b601454909250612b8b91506001600160a01b031687848487611258565b505b60005461289a906001600160a01b031686868686612cd9565b6013546001600160a01b0385811691161415612cd057600054612bd69086906001600160a01b0316858585612cd9565b600054601354612bf2916001600160a01b039081169116611e7f565b6013546000546040516370a0823160e01b81526001600160a01b039283169263049878f39216906370a0823190612c2d903090600401614fd9565b60206040518083038186803b158015612c4557600080fd5b505afa158015612c59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612c7d9190810190614b3d565b6040518263ffffffff1660e01b8152600401612c9991906152cf565b600060405180830381600087803b158015612cb357600080fd5b505af1158015612cc7573d6000803e3d6000fd5b50505050611284565b61128485858585855b836001600160a01b0316856001600160a01b03161415612cf857611284565b612d0981604063ffffffff61194c16565b612f0d576012546001600160a01b0386811691161415612e165760125460405163ef693bed60e01b81526001600160a01b039091169063ef693bed90612d559030908790600401615002565b600060405180830381600087803b158015612d6f57600080fd5b505af1158015612d83573d6000803e3d6000fd5b50506000546040516370a0823160e01b815261178493506001600160a01b039091169150869082906370a0823190612dbf903090600401614fd9565b60206040518083038186803b158015612dd757600080fd5b505afa158015612deb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e0f9190810190614b3d565b8585612f16565b6012546001600160a01b0385811691161415612f0d57600054612e469086906001600160a01b0316858585612f16565b600054601254612e62916001600160a01b039081169116611e7f565b6012546000546040516370a0823160e01b81526001600160a01b0392831692633b4da69f9230929116906370a0823190612ea0908490600401614fd9565b60206040518083038186803b158015612eb857600080fd5b505afa158015612ecc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612ef09190810190614b3d565b6040518363ffffffff1660e01b8152600401612c99929190615002565b61128485858585855b612f28856001600160a01b031661111e565b158015612f445750612f42846001600160a01b031661111e565b155b8015612f5d5750612f5d8161020063ffffffff61194c16565b1561305e5760608251604051908082528060200260200182016040528015612f8f578160200160208202803883390190505b50905060005b8351811015612fd457838181518110612faa57fe5b602002602001015160ff16828281518110612fc157fe5b6020908102919091010152600101612f95565b50612ff68673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee868486613368565b60005b835181101561303c57600884828151811061301057fe5b6020026020010151901c60ff1682828151811061302957fe5b6020908102919091010152600101612ff9565b5061289a73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee86478486613368565b6000546001600160a01b0386811691161480159061308a57506000546001600160a01b03858116911614155b80156130a457506130a4816201000063ffffffff61194c16565b1561320a57606082516040519080825280602002602001820160405280156130d6578160200160208202803883390190505b50905060005b835181101561311b578381815181106130f157fe5b602002602001015160ff1682828151811061310857fe5b60209081029190910101526001016130dc565b506000546131369087906001600160a01b0316868486613368565b60005b835181101561317c57600884828151811061315057fe5b6020026020010151901c60ff1682828151811061316957fe5b6020908102919091010152600101613139565b506000546040516370a0823160e01b815261289a916001600160a01b031690879082906370a08231906131b3903090600401614fd9565b60206040518083038186803b1580156131cb57600080fd5b505afa1580156131df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506132039190810190614b3d565b8486613368565b6001546001600160a01b0386811691161480159061323657506001546001600160a01b03858116911614155b80156132505750613250816202000063ffffffff61194c16565b1561335f5760608251604051908082528060200260200182016040528015613282578160200160208202803883390190505b50905060005b83518110156132c75783818151811061329d57fe5b602002602001015160ff168282815181106132b457fe5b6020908102919091010152600101613288565b506001546132e29087906001600160a01b0316868486613368565b60005b83518110156133285760088482815181106132fc57fe5b6020026020010151901c60ff1682828151811061331557fe5b60209081029190910101526001016132e5565b506001546040516370a0823160e01b815261289a916001600160a01b031690879082906370a08231906131b3903090600401614fd9565b61128485858585855b836001600160a01b0316856001600160a01b0316141561338757611284565b61338f61470d565b506040805161012081018252613504815261377860208201526138b591810191909152613c426060820152613ed1608082015261401860a082015261416760c082015261430960e08201526144ab610100820152600080805b60098110156134415760008682815181106133ff57fe5b602002602001015111156134395761343386828151811061341c57fe5b6020026020010151846146c990919063ffffffff16565b92508091505b6001016133e8565b50600082116134625760405162461bcd60e51b8152600401610ff690615262565b8560005b60098110156134f85786818151811061347b57fe5b602002602001015160001415613490576134f0565b60006134bc856110ae8a85815181106134a557fe5b60200260200101518c6113ac90919063ffffffff16565b9050838214156134c95750815b80830392506134ed8b8b838986600981106134e057fe5b602002015163ffffffff16565b50505b600101613466565b50505050505050505050565b6000816135196001600160a01b03861661111e565b613641576009546040516303795fb160e11b81526000916001600160a01b0316906306f2bf629061354e90899060040161509f565b60206040518083038186803b15801561356657600080fd5b505afa15801561357a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061359e91908101906149a2565b90506001600160a01b0381161561363f576135b98682611e7f565b6040516395e3c50b60e01b81526001600160a01b038216906395e3c50b906135ea90859060019042906004016152fd565b602060405180830381600087803b15801561360457600080fd5b505af1158015613618573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061363c9190810190614b3d565b91505b505b613653846001600160a01b031661111e565b611944576009546040516303795fb160e11b81526000916001600160a01b0316906306f2bf629061368890889060040161509f565b60206040518083038186803b1580156136a057600080fd5b505afa1580156136b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506136d891908101906149a2565b90506001600160a01b0381161561376f5760405163f39b5b9b60e01b81526001600160a01b0382169063f39b5b9b90849061371a906001904290600401615213565b6020604051808303818588803b15801561373357600080fd5b505af1158015613747573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525061376c9190810190614b3d565b91505b50949350505050565b6008546000906137929085906001600160a01b0316611e7f565b6008546001600160a01b03908116906329589f61906137b290871661111e565b6137bd5760006137bf565b835b6137d1876001600160a01b031661111e565b6137db57866137f1565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b85613804886001600160a01b031661111e565b61380e5787613824565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b30600160ff1b6000734d37f28d2db99e8d35a6c725a5f1749a085850a36040518963ffffffff1660e01b8152600401613863979695949392919061510a565b6020604051808303818588803b15801561387c57600080fd5b505af1158015613890573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506119449190810190614b3d565b60006138c9846001600160a01b031661111e565b1561393857600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561391e57600080fd5b505af1158015613932573d6000803e3d6000fd5b50505050505b600a54604051632ecd14d360e21b81526000916001600160a01b03169063bb34534c90613967906004016152a2565b60206040518083038186803b15801561397f57600080fd5b505afa158015613993573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506139b791908101906148d9565b600b549091506060906001600160a01b03908116906375e1cc82906139dd90891661111e565b6139e757876139f4565b6007546001600160a01b03165b613a06886001600160a01b031661111e565b613a105787613a1d565b6007546001600160a01b03165b6040518363ffffffff1660e01b8152600401613a3a9291906150ad565b60006040518083038186803b158015613a5257600080fd5b505afa158015613a66573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613a8e91908101906148f7565b9050613ac2613aa5876001600160a01b031661111e565b613aaf5786613abc565b6007546001600160a01b03165b83611e7f565b6040516331ee892f60e21b81526000906001600160a01b0384169063c7ba24bc90613af69085908990600190600401615061565b602060405180830381600087803b158015613b1057600080fd5b505af1158015613b24573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613b489190810190614b3d565b9050613b5c866001600160a01b031661111e565b15613c38576007546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a0823190613b99903090600401614fd9565b60206040518083038186803b158015613bb157600080fd5b505afa158015613bc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613be99190810190614b3d565b6040518263ffffffff1660e01b8152600401613c0591906152cf565b600060405180830381600087803b158015613c1f57600080fd5b505af1158015613c33573d6000803e3d6000fd5b505050505b9695505050505050565b6000613c56846001600160a01b031661111e565b15613cc557600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015613cab57600080fd5b505af1158015613cbf573d6000803e3d6000fd5b50505050505b613d02613cda856001600160a01b031661111e565b613ce45784613cf1565b6006546001600160a01b03165b600c546001600160a01b0316611e7f565b600c546000906001600160a01b0390811690630621b4f690613d2590881661111e565b613d2f5786613d3c565b6006546001600160a01b03165b85613d4f886001600160a01b031661111e565b613d595787613d66565b6006546001600160a01b03165b60016040518563ffffffff1660e01b8152600401613d879493929190615185565b602060405180830381600087803b158015613da157600080fd5b505af1158015613db5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613dd99190810190614b3d565b9050613ded846001600160a01b031661111e565b15611944576006546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a0823190613e2a903090600401614fd9565b60206040518083038186803b158015613e4257600080fd5b505afa158015613e56573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613e7a9190810190614b3d565b6040518263ffffffff1660e01b8152600401613e9691906152cf565b600060405180830381600087803b158015613eb057600080fd5b505af1158015613ec4573d6000803e3d6000fd5b5050505090509392505050565b60015460009081906001600160a01b03868116911614613ef2576000613ef5565b60025b6000546001600160a01b03878116911614613f11576000613f14565b60015b600154910160ff1691506000906001600160a01b0390811690861614613f3b576000613f3e565b60025b6000546001600160a01b03878116911614613f5a576000613f5d565b60015b0160ff16905081600f0b60001480613f78575080600f0b6000145b15613f8857600092505050611328565b600d54613f9f9087906001600160a01b0316611e7f565b600d54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed690613fdd90600019808701919086019089906000906004016151eb565b600060405180830381600087803b158015613ff757600080fd5b505af115801561400b573d6000803e3d6000fd5b5050505050509392505050565b60025460009081906001600160a01b0386811691161461403957600061403c565b60035b6001546001600160a01b0387811691161461405857600061405b565b60025b6000546001600160a01b0388811691161461407757600061407a565b60015b60025491019190910160ff1691506000906001600160a01b03908116908616146140a55760006140a8565b60035b6001546001600160a01b038781169116146140c45760006140c7565b60025b6000546001600160a01b038881169116146140e35760006140e6565b60015b010160ff16905081600f0b60001480614102575080600f0b6000145b1561411257600092505050611328565b600e546141299087906001600160a01b0316611e7f565b600e54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed690613fdd90600019808701919086019089906000906004016151eb565b60035460009081906001600160a01b0386811691161461418857600061418b565b60045b6002546001600160a01b038781169116146141a75760006141aa565b60035b6001546001600160a01b038881169116146141c65760006141c9565b60025b6000546001600160a01b038981169116146141e55760006141e8565b60015b01010160ff1690506000600360009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b03161461422757600061422a565b60045b6002546001600160a01b03878116911614614246576000614249565b60035b6001546001600160a01b03888116911614614265576000614268565b60025b6000546001600160a01b03898116911614614284576000614287565b60015b01010160ff16905081600f0b600014806142a4575080600f0b6000145b156142b457600092505050611328565b600f546142cb9087906001600160a01b0316611e7f565b600f54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed690613fdd90600019808701919086019089906000906004016151eb565b60045460009081906001600160a01b0386811691161461432a57600061432d565b60045b6002546001600160a01b0387811691161461434957600061434c565b60035b6001546001600160a01b0388811691161461436857600061436b565b60025b6000546001600160a01b0389811691161461438757600061438a565b60015b01010160ff1690506000600460009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b0316146143c95760006143cc565b60045b6002546001600160a01b038781169116146143e85760006143eb565b60035b6001546001600160a01b0388811691161461440757600061440a565b60025b6000546001600160a01b03898116911614614426576000614429565b60015b01010160ff16905081600f0b60001480614446575080600f0b6000145b1561445657600092505050611328565b60105461446d9087906001600160a01b0316611e7f565b601054604051635320bf6b60e11b81526001600160a01b039091169063a6417ed690613fdd90600019808701919086019089906000906004016151eb565b60055460009081906001600160a01b038681169116146144cc5760006144cf565b60055b6003546001600160a01b038781169116146144eb5760006144ee565b60045b6002546001600160a01b0388811691161461450a57600061450d565b60035b6001546001600160a01b0389811691161461452957600061452c565b60025b6000546001600160a01b038a811691161461454857600061454b565b60015b0101010160ff1690506000600560009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b03161461458b57600061458e565b60055b6003546001600160a01b038781169116146145aa5760006145ad565b60045b6002546001600160a01b038881169116146145c95760006145cc565b60035b6001546001600160a01b038981169116146145e85760006145eb565b60025b6000546001600160a01b038a811691161461460757600061460a565b60015b0101010160ff16905081600f0b60001480614628575080600f0b6000145b1561463857600092505050611328565b6005546001600160a01b0387811691161480159061466457506005546001600160a01b03868116911614155b1561467457600092505050611328565b60115461468b9087906001600160a01b0316611e7f565b601154604051635320bf6b60e11b81526001600160a01b039091169063a6417ed690613fdd90600019808701919086019089906000906004016151eb565b6000828201838110156113285760405162461bcd60e51b8152600401610ff690615232565b604051806101400160405280600a906020820280388339509192915050565b6040518061012001604052806009905b61473681526020019060019003908161471d5790505090565bfe5b805161115281615401565b600082601f83011261475457600080fd5b81516147676147628261533f565b615318565b9150818183526020840193506020810190508385602084028201111561478c57600080fd5b60005b838110156147b857816147a28882614738565b845250602092830192919091019060010161478f565b5050505092915050565b600082601f8301126147d357600080fd5b81356147e16147628261533f565b9150818183526020840193506020810190508385602084028201111561480657600080fd5b60005b838110156147b8578161481c88826148c3565b8452506020928301929190910190600101614809565b600082601f83011261484357600080fd5b81516148516147628261533f565b9150818183526020840193506020810190508385602084028201111561487657600080fd5b60005b838110156147b8578161488c88826148ce565b8452506020928301929190910190600101614879565b805161115281615418565b803561115281615421565b805161115281615421565b80356111528161542a565b80516111528161542a565b6000602082840312156148eb57600080fd5b60006119448484614738565b60006020828403121561490957600080fd5b815167ffffffffffffffff81111561492057600080fd5b61194484828501614743565b60006020828403121561493e57600080fd5b600061194484846148a2565b6000806040838503121561495d57600080fd5b600061496985856148a2565b925050602061497a858286016148ce565b9150509250929050565b60006020828403121561499657600080fd5b600061194484846148ad565b6000602082840312156149b457600080fd5b600061194484846148b8565b60008060008060008060c087890312156149d957600080fd5b60006149e589896148ad565b96505060206149f689828a016148ad565b9550506040614a0789828a016148c3565b9450506060614a1889828a016148c3565b935050608087013567ffffffffffffffff811115614a3557600080fd5b614a4189828a016147c2565b92505060a0614a5289828a016148c3565b9150509295509295509295565b600080600080600060a08688031215614a7757600080fd5b6000614a8388886148ad565b9550506020614a94888289016148ad565b9450506040614aa5888289016148c3565b9350506060614ab6888289016148c3565b9250506080614ac7888289016148c3565b9150509295509295909350565b60008060008060008060c08789031215614aed57600080fd5b6000614af989896148ad565b9650506020614b0a89828a016148ad565b9550506040614b1b89828a016148c3565b9450506060614b2c89828a016148c3565b9350506080614a4189828a016148c3565b600060208284031215614b4f57600080fd5b600061194484846148ce565b60008060408385031215614b6e57600080fd5b6000614b7a85856148ce565b925050602083015167ffffffffffffffff811115614b9757600080fd5b61497a85828601614832565b6000614baf8383614bd2565b505060200190565b6000614baf8383614fb6565b614bcc816153aa565b82525050565b614bcc81615373565b6000614be682615366565b614bf0818561536a565b9350614bfb83615360565b8060005b83811015614c29578151614c138882614ba3565b9750614c1e83615360565b925050600101614bff565b509495945050505050565b6000614c3f82615366565b614c49818561536a565b9350614c5483615360565b8060005b83811015614c29578151614c6c8882614bb7565b9750614c7783615360565b925050600101614c58565b6000614c8d82615366565b614c978185610c2d565b9350614ca78185602086016153cb565b9290920192915050565b614bcc81615383565b614bcc8161538e565b614bcc816153b5565b614bcc816153c0565b6000614ce082615366565b614cea818561536a565b9350614cfa8185602086016153cb565b614d03816153f7565b9093019392505050565b6000614d1a601b8361536a565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b6000614d5360358361536a565b7f4f6e6553706c69743a2061637475616c2072657475726e20616d6f756e74206981527439903632b9b9903a3430b71036b4b72932ba3ab93760591b602082015260400192915050565b6000614daa60208361536a565b7f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815260200192915050565b6000614de3602f8361536a565b7f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f81526e6e7461696e206e6f6e2d7a65726f7360881b602082015260400192915050565b6000614e34602b8361536a565b7f57726f6e6720757365616765206f66204554482e756e6976657273616c54726181526a6e7366657246726f6d282960a81b602082015260400192915050565b6000614e8160218361536a565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b600061115260008361536a565b6000614ed1602a8361536a565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e8152691bdd081cdd58d8d9595960b21b602082015260400192915050565b6c42616e636f724e6574776f726b60981b9052565b6000614f3260368361536a565b7f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f81527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b602082015260400192915050565b6000614f8a601f8361536a565b7f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400815260200192915050565b614bcc816153a7565b60006113288284614c82565b602081016111528284614bd2565b602081016111528284614bc3565b60408101614ff58285614bc3565b6113286020830184614bd2565b604081016150108285614bc3565b6113286020830184614fb6565b60408101614ff58285614bd2565b606081016150398286614bd2565b6150466020830185614bd2565b6119446040830184614fb6565b604081016150108285614bd2565b606080825281016150728186614bdb565b90506150816020830185614fb6565b6119446040830184614cc3565b602080825281016113288184614c34565b602081016111528284614cb1565b604081016150bb8285614cb1565b6113286020830184614cb1565b60a081016150d68288614cb1565b6150e36020830187614cb1565b6150f06040830186614fb6565b6150fd6060830185614fb6565b613c386080830184614fb6565b6101008101615119828a614cb1565b6151266020830189614fb6565b6151336040830188614cb1565b6151406060830187614bc3565b61514d6080830186614cc3565b61515a60a0830185614cc3565b61516760c0830184614bc3565b81810360e083015261517881614eb7565b9998505050505050505050565b608081016151938287614cb1565b6151a06020830186614fb6565b6151ad6040830185614cb1565b6151ba6060830184614cc3565b95945050505050565b606081016151d18286614cb1565b6151de6020830185614fb6565b6119446040830184614ccc565b608081016151f98287614cba565b6152066020830186614cba565b6151ad6040830185614fb6565b604081016150108285614cc3565b602080825281016113288184614cd5565b6020808252810161115281614d0d565b6020808252810161115281614d46565b6020808252810161115281614d9d565b6020808252810161115281614dd6565b6020808252810161115281614e27565b6020808252810161115281614e74565b6020808252810161115281614ec4565b60208101610c2d82614f10565b6020808252810161115281614f25565b6020808252810161115281614f7d565b602081016111528284614fb6565b604081016152eb8285614fb6565b81810360208301526119448184614c34565b6060810161530b8286614fb6565b6150466020830185614cc3565b60405181810167ffffffffffffffff8111828210171561533757600080fd5b604052919050565b600067ffffffffffffffff82111561535657600080fd5b5060209081020190565b60200190565b5190565b90815260200190565b60006111528261539b565b151590565b600061115282615373565b600f0b90565b61ffff1690565b6001600160a01b031690565b90565b600061115282615383565b6000611152826153a7565b600061115282615394565b60005b838110156153e65781810151838201526020016153ce565b838111156112525750506000910152565b601f01601f191690565b61540a81615373565b811461541557600080fd5b50565b61540a8161537e565b61540a81615383565b61540a816153a756fea365627a7a72315820386acf115a1df2daad0f26f1cd6cad922ffe149c2ff6f15b2eef8b7081d00ad36c6578706572696d656e74616cf564736f6c63430005110040 \ No newline at end of file +6080604052600080546001600160a01b0319908116736b175474e89094c44da98b954eedeac495271d0f1790915560018054821673a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4817905560028054821673dac17f958d2ee523a2206206994597c13d831ec71790556003805482166e085d4780b73119b644ae5ecd22b376179055600480548216734fabb145d64652a948d72533023f6e7a623c7c531790556005805482167357ab1ec28d129707052df4df418d58a2d46d5f5117905560068054821673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc217905560078054821673c0829421c1d260bd3cb3e0f06cfe2d52db2ce31517905560088054821673818e6fecd516ecc3849daf6845e3ec868087b75517905560098054821673c0a47dfe034b400b47bdad5fecda2621de6c4d95179055600a805482167352ae12abe5d8bd778bd5397f99ca900624cfadd4179055600b80548216736f0cd8c4f6f06eab664c7e3031909452b4b72861179055600c8054821673794e6e91555438afc3ccf1c5076a74f42133d08d179055600d8054821673a2b47e3d5c44877cca798226b7b8118f9bfb7a56179055600e805482167352ea46506b9cc5ef470c5bf89f17dc28bb35d85c179055600f805482167345f783cce6b7ff23b2ab2d70e416cdb7d6055f511790556010805482167379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27179055601180548216733b12e1fbb468bea80b492d635976809bf950186c1790556012805482167306af07097c9eeb7fd685c692751d5c66db49c215179055601380548216736a4ffaafa8dd400676df8076ad6c724867b0e2e817905560148054821673b683d83a532e2cb7dfa5275eed3698436371cc9f17905560158054821673398ec7346dcd622edc5ae82352f02be94c62d119179055601680548216733d9819210a31b4961b30ef54be2aed79b9c9cd3b179055601780548216734ddc2d193948926d02f9b1fe9e1daa0718270ed517905560188054821673f6e2d7f616b67e46d708e4410746e9aab3a4c5181790556019805490911673524619eb9b4cdffa7da13029b33f24635478afc01790553480156200032457600080fd5b5060405162006a6638038062006a66833981016040819052620003479162000380565b601a80546001600160a01b0319166001600160a01b0392909216919091179055620003e9565b80516200037a81620003cf565b92915050565b6000602082840312156200039357600080fd5b6000620003a184846200036d565b949350505050565b60006200037a82620003c3565b60006200037a82620003a9565b6001600160a01b031690565b620003da81620003b6565b8114620003e657600080fd5b50565b61666d80620003f96000396000f3fe6080604052600436106103355760003560e01c806372b6f1bf116101ab578063c762a46c116100f7578063dc1536b211610095578063f4b9fa751161006f578063f4b9fa75146107c4578063f56e281f146107d9578063f69e2046146107ee578063fbe4ed951461080357610335565b8063dc1536b21461076f578063e2a7515e14610784578063f484966b1461079757610335565b8063c9b42c67116100d1578063c9b42c671461071b578063cede5f6a14610730578063d393c3e914610745578063d77366a41461075a57610335565b8063c762a46c146106dc578063c77b9de6146106f1578063c92577751461070657610335565b80638bdb2afa11610164578063b0a7ef291161013e578063b0a7ef2914610688578063b3bc78441461069d578063b69d0456146106b2578063c11f4f11146106c757610335565b80638bdb2afa14610649578063a1b4d0111461065e578063a734f06e1461067357610335565b806372b6f1bf146105c057806375a8b012146105e057806375b5be2d146105f55780637a88bdbd1461060a578063819faf7b1461061f578063851954fa1461063457610335565b80633e413bee116102855780635aa8fb481161022357806364ec4e5c116101fd57806364ec4e5c1461056e5780636b5a4ca2146105835780636b9589aa146105985780636cbc4a6e146105ab57610335565b80635aa8fb481461052f5780635ae51b82146105445780635c0cb4791461055957610335565b806344211d621161025f57806344211d62146104db5780634a7101d5146104f05780634b57b0be1461050557806351f1985c1461051a57610335565b80633e413bee146104915780634037f967146104a6578063423d03f9146104c657610335565b806322320c98116102f25780632f48ab7d116102cc5780632f48ab7d1461043d57806334b4dabb14610452578063372a26cb146104675780633ca5b2341461047c57610335565b806322320c98146103fe5780632d3b5207146104135780632e707bd21461042857610335565b8063085e2c5b1461034457806312dea1601461037b5780631388b4201461039d57806313989140146103b25780632113240d146103d457806321a360f5146103e9575b3332141561034257600080fd5b005b34801561035057600080fd5b5061036461035f366004615b93565b610818565b6040516103729291906164b9565b60405180910390f35b34801561038757600080fd5b506103906108b8565b604051610372919061620f565b3480156103a957600080fd5b506103906108c7565b3480156103be57600080fd5b506103c76108d6565b60405161037291906164ab565b3480156103e057600080fd5b506103c76108dc565b3480156103f557600080fd5b506103c76108e2565b34801561040a57600080fd5b506103906108eb565b34801561041f57600080fd5b506103c76108fa565b34801561043457600080fd5b506103c7610903565b34801561044957600080fd5b50610390610908565b34801561045e57600080fd5b506103c7610917565b34801561047357600080fd5b5061039061091c565b34801561048857600080fd5b5061039061092b565b34801561049d57600080fd5b5061039061093a565b3480156104b257600080fd5b506103906104c1366004615ab8565b610949565b3480156104d257600080fd5b50610390610c32565b3480156104e757600080fd5b506103c7610c41565b3480156104fc57600080fd5b506103c7610c46565b34801561051157600080fd5b50610390610c4b565b34801561052657600080fd5b50610390610c5a565b34801561053b57600080fd5b506103c7610c69565b34801561055057600080fd5b506103c7610c6f565b34801561056557600080fd5b506103c7610c75565b34801561057a57600080fd5b506103c7610c7a565b34801561058f57600080fd5b50610390610c81565b6103426105a6366004615c08565b610c90565b3480156105b757600080fd5b506103c7610cb9565b3480156105cc57600080fd5b506103906105db366004615ab8565b610cc0565b3480156105ec57600080fd5b506103c7610eaa565b34801561060157600080fd5b50610390610eb0565b34801561061657600080fd5b506103c7610ebf565b34801561062b57600080fd5b50610390610ec4565b34801561064057600080fd5b50610390610ed3565b34801561065557600080fd5b50610390610ee2565b34801561066a57600080fd5b50610390610ef1565b34801561067f57600080fd5b50610390610f00565b34801561069457600080fd5b506103c7610f18565b3480156106a957600080fd5b506103c7610f1e565b3480156106be57600080fd5b50610390610f27565b3480156106d357600080fd5b50610390610f36565b3480156106e857600080fd5b506103c7610f45565b3480156106fd57600080fd5b506103c7610f4a565b34801561071257600080fd5b50610390610f50565b34801561072757600080fd5b506103c7610f5f565b34801561073c57600080fd5b50610390610f66565b34801561075157600080fd5b506103c7610f75565b34801561076657600080fd5b50610390610f7c565b34801561077b57600080fd5b506103c7610f8b565b610342610792366004615af4565b610f91565b3480156107a357600080fd5b506107b76107b2366004615b93565b611058565b60405161037291906161fe565b3480156107d057600080fd5b506103906110ec565b3480156107e557600080fd5b506103c76110fb565b3480156107fa57600080fd5b50610390611100565b34801561080f57600080fd5b5061039061110f565b601a5460405163085e2c5b60e01b81526000916060916001600160a01b039091169063085e2c5b90610856908a908a908a908a908a906004016162c7565b60006040518083038186803b15801561086e57600080fd5b505afa158015610882573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526108aa9190810190615ccb565b915091509550959350505050565b600a546001600160a01b031681565b600c546001600160a01b031681565b61200081565b61800081565b64020000000081565b6011546001600160a01b031681565b64010000000081565b608081565b6002546001600160a01b031681565b604081565b6010546001600160a01b031681565b6004546001600160a01b031681565b6001546001600160a01b031681565b600061095d826001600160a01b031661111e565b1561096b5750600019610c2d565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b17905290516000916060916001600160a01b03861691611388916109b4919061612f565b6000604051808303818686fa925050503d80600081146109f0576040519150601f19603f3d011682016040523d82523d6000602084013e6109f5565b606091505b509150915081610a0b5760001992505050610c2d565b6000805b6007835103811015610b6457828160000181518110610a2a57fe5b6020910101516001600160f81b031916602360f91b148015610a6d5750828160010181518110610a5657fe5b6020910101516001600160f81b031916607560f81b145b8015610a9a5750828160020181518110610a8357fe5b6020910101516001600160f81b031916601b60fa1b145b8015610ac75750828160030181518110610ab057fe5b6020910101516001600160f81b031916606360f81b145b8015610af45750828160040181518110610add57fe5b6020910101516001600160f81b031916603960f91b145b8015610b215750828160050181518110610b0a57fe5b6020910101516001600160f81b031916607560f81b145b8015610b4e5750828160060181518110610b3757fe5b6020910101516001600160f81b031916606d60f81b145b15610b5c5760019150610b64565b600101610a0f565b5080610b77576000199350505050610c2d565b60408051600481526024810182526020810180516001600160e01b031663797bf38560e01b17905290516001600160a01b0387169161138891610bba919061612f565b6000604051808303818686fa925050503d8060008114610bf6576040519150601f19603f3d011682016040523d82523d6000602084013e610bfb565b606091505b50909350915082610c13576000199350505050610c2d565b81806020019051610c279190810190615ad6565b93505050505b919050565b600f546001600160a01b031681565b601081565b602081565b6006546001600160a01b031681565b600d546001600160a01b031681565b61400081565b61080081565b600881565b6202000081565b6014546001600160a01b031681565b6060610c9f8787878686610818565b915050610cb0878787878587610f91565b50505050505050565b6208000081565b6000610cd4826001600160a01b031661111e565b15610ce25750600019610c2d565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b17905290516000916060916001600160a01b0386169161138891610d2b919061612f565b6000604051808303818686fa925050503d8060008114610d67576040519150601f19603f3d011682016040523d82523d6000602084013e610d6c565b606091505b509150915081610d825760001992505050610c2d565b6000805b6004835103811015610e5457828160000181518110610da157fe5b6020910101516001600160f81b031916604160f81b148015610de45750828160010181518110610dcd57fe5b6020910101516001600160f81b031916606160f81b145b8015610e115750828160020181518110610dfa57fe5b6020910101516001600160f81b031916603b60f91b145b8015610e3e5750828160030181518110610e2757fe5b6020910101516001600160f81b031916606560f81b145b15610e4c5760019150610e54565b600101610d86565b5080610e67576000199350505050610c2d565b60408051600481526024810182526020810180516001600160e01b0316632274683f60e21b17905290516001600160a01b0387169161138891610bba919061612f565b61040081565b6003546001600160a01b031681565b600281565b6015546001600160a01b031681565b6007546001600160a01b031681565b6009546001600160a01b031681565b6017546001600160a01b031681565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b6012546001600160a01b031681565b6013546001600160a01b031681565b600181565b61020081565b6005546001600160a01b031681565b6204000081565b600e546001600160a01b031681565b6201000081565b6008546001600160a01b031681565b61010081565b610fac6001600160a01b03871633308763ffffffff61115816565b610fb98686868585611258565b6000610fd46001600160a01b0387163063ffffffff61128b16565b905083811015610fff5760405162461bcd60e51b8152600401610ff69061641e565b60405180910390fd5b6110196001600160a01b038716338363ffffffff61132f16565b5061104e336110376001600160a01b038a163063ffffffff61128b16565b6001600160a01b038a16919063ffffffff61132f16565b5050505050505050565b606082604051908082528060200260200182016040528015611084578160200160208202803883390190505b50905060005b838110156110e2576110c287876110ba876110ae8a6001880163ffffffff6113ac16565b9063ffffffff6113e616565b600187610818565b508282815181106110cf57fe5b602090810291909101015260010161108a565b5095945050505050565b6000546001600160a01b031681565b600481565b6016546001600160a01b031681565b601a546001600160a01b031681565b60006001600160a01b038216158061115257506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b92915050565b8061116257611252565b61116b8461111e565b15611237576001600160a01b038316331480156111885750803410155b6111a45760405162461bcd60e51b8152600401610ff69061644e565b6001600160a01b03821630146111ec576040516001600160a01b0383169082156108fc029083906000818181858888f193505050501580156111ea573d6000803e3d6000fd5b505b8034111561123257336108fc611208348463ffffffff61142816565b6040518115909202916000818181858888f19350505050158015611230573d6000803e3d6000fd5b505b611252565b6112526001600160a01b03851684848463ffffffff61146a16565b50505050565b836001600160a01b0316856001600160a01b0316141561127757611284565b61128485858585856114c5565b5050505050565b60006112968361111e565b156112ac57506001600160a01b03811631611152565b6040516370a0823160e01b81526001600160a01b038416906370a08231906112d890859060040161613b565b60206040518083038186803b1580156112f057600080fd5b505afa158015611304573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506113289190810190615cad565b9392505050565b60008161133e57506001611328565b6113478461111e565b15611388576040516001600160a01b0384169083156108fc029084906000818181858888f19350505050158015611382573d6000803e3d6000fd5b50611328565b6113a26001600160a01b038516848463ffffffff61163016565b5060019392505050565b6000826113bb57506000611152565b828202828482816113c857fe5b04146113285760405162461bcd60e51b8152600401610ff69061645e565b600061132883836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250611657565b600061132883836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061168e565b6040516112529085906323b872dd60e01b9061148e9087908790879060240161619b565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526116ba565b836001600160a01b0316856001600160a01b031614156114e457611284565b6114f68161010063ffffffff61179f16565b611623576018546040516302091f7b60e51b81526001600160a01b0390911690634123ef609061152a90889060040161620f565b60206040518083038186803b15801561154257600080fd5b505afa158015611556573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061157a9190810190615a60565b156115915761158c85858585856117a5565b611284565b6018546040516302091f7b60e51b81526001600160a01b0390911690634123ef60906115c190879060040161620f565b60206040518083038186803b1580156115d957600080fd5b505afa1580156115ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116119190810190615a60565b156116235761158c8585858585611b33565b6112848585858585612136565b60405161165290849063a9059cbb60e01b9061148e90869086906024016161c3565b505050565b600081836116785760405162461bcd60e51b8152600401610ff691906163fd565b50600083858161168457fe5b0495945050505050565b600081848411156116b25760405162461bcd60e51b8152600401610ff691906163fd565b505050900390565b6116cc826001600160a01b0316612143565b6116e85760405162461bcd60e51b8152600401610ff69061649b565b60006060836001600160a01b031683604051611704919061612f565b6000604051808303816000865af19150503d8060008114611741576040519150601f19603f3d011682016040523d82523d6000602084013e611746565b606091505b5091509150816117685760405162461bcd60e51b8152600401610ff69061642e565b80511561125257808060200190516117839190810190615a60565b6112525760405162461bcd60e51b8152600401610ff69061646e565b16151590565b6117ad615748565b6117b68661217f565b905060608160000151516040519080825280602002602001820160405280156117e9578160200160208202803883390190505b509050606082600001515160405190808252806020026020018201604052801561182757816020015b60608152602001906001900390816118125790505b50905060005b83515161ffff821610156119a4578551604051908082528060200260200182016040528015611866578160200160208202803883390190505b50828261ffff168151811061187757fe5b602002602001018190525083600001518161ffff168151811061189657fe5b6020026020010151600001516001600160a01b03166370a08231336040518263ffffffff1660e01b81526004016118cd9190616149565b60206040518083038186803b1580156118e557600080fd5b505afa1580156118f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061191d9190810190615cad565b838261ffff168151811061192d57fe5b602090810291909101015260005b865181101561199b578160080261ffff1687828151811061195857fe5b6020026020010151901c60ff16838361ffff168151811061197557fe5b6020026020010151828151811061198857fe5b602090810291909101015260010161193b565b5060010161182d565b5082602001516001600160a01b031663415f1240876040518263ffffffff1660e01b81526004016119d591906164ab565b600060405180830381600087803b1580156119ef57600080fd5b505af1158015611a03573d6000803e3d6000fd5b505084515160009250159050611b2857600084600001518261ffff1681518110611a2957fe5b6020026020010151600001516001600160a01b03166370a08231336040518263ffffffff1660e01b8152600401611a609190616149565b60206040518083038186803b158015611a7857600080fd5b505afa158015611a8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ab09190810190615cad565b9050611b1e85600001518361ffff1681518110611ac957fe5b6020026020010151600001518a611b00878661ffff1681518110611ae957fe5b60200260200101518561142890919063ffffffff16565b868661ffff1681518110611b1057fe5b60200260200101518a612136565b5050505050611284565b505050505050505050565b600019611b3e615748565b611b478661217f565b90506060816000015151604051908082528060200260200182016040528015611b7a578160200160208202803883390190505b50905060005b82515161ffff82161015611f37576000611bbe8885600001518461ffff1681518110611ba857fe5b6020026020010151602001518660400151612228565b9050600084600001518361ffff1681518110611bd657fe5b6020026020010151600001516001600160a01b03166370a08231336040518263ffffffff1660e01b8152600401611c0d9190616149565b60206040518083038186803b158015611c2557600080fd5b505afa158015611c39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c5d9190810190615cad565b905060005b8851811015611cab578360080261ffff16898281518110611c7f57fe5b6020026020010151901c60ff16898281518110611c9857fe5b6020908102919091010152600101611c62565b50611cd88b86600001518561ffff1681518110611cc457fe5b602002602001015160000151848b8b612136565b600085600001518461ffff1681518110611cee57fe5b6020026020010151600001516001600160a01b03166370a08231336040518263ffffffff1660e01b8152600401611d259190616149565b60206040518083038186803b158015611d3d57600080fd5b505afa158015611d51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d759190810190615cad565b9050611ea986600001518561ffff1681518110611d8e57fe5b6020026020010151600001516001600160a01b03166370a0823188602001516040518263ffffffff1660e01b8152600401611dc9919061613b565b60206040518083038186803b158015611de157600080fd5b505afa158015611df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e199190810190615cad565b6110ae611e2c848663ffffffff61142816565b8e6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611e6557600080fd5b505afa158015611e79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e9d9190810190615cad565b9063ffffffff6113ac16565b858561ffff1681518110611eb957fe5b60200260200101818152505086858561ffff1681518110611ed657fe5b60200260200101511015611eff57848461ffff1681518110611ef457fe5b602002602001015196505b611f2c86600001518561ffff1681518110611f1657fe5b602002602001015160000151876020015161223e565b505050600101611b80565b5081602001516001600160a01b031663ca1d209d846040518263ffffffff1660e01b8152600401611f6891906164ab565b600060405180830381600087803b158015611f8257600080fd5b505af1158015611f96573d6000803e3d6000fd5b506000925050505b82515161ffff82161015611b2857600083600001518261ffff1681518110611fc257fe5b6020026020010151600001516001600160a01b03166370a0823185602001516040518263ffffffff1660e01b8152600401611ffd919061613b565b60206040518083038186803b15801561201557600080fd5b505afa158015612029573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061204d9190810190615cad565b905060006120f38a6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561208d57600080fd5b505afa1580156120a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506120c59190810190615cad565b6110ae84611e9d8a898961ffff16815181106120dd57fe5b602002602001015161142890919063ffffffff16565b9050801561212c5761212a856020015186600001518561ffff168151811061211757fe5b6020026020010151600001518c846122fb565b505b5050600101611f9e565b6112848585858585612497565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061217757508115155b949350505050565b612187615748565b6000826001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156121c257600080fd5b505afa1580156121d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506121fa9190810190615ad6565b90506060600061220983612757565b9085526001600160a01b03909316602085015250506040820152919050565b6000612177826110ae868663ffffffff6113ac16565b612250826001600160a01b031661111e565b6122f757604051636eb1769f60e11b815260ff906001600160a01b0384169063dd62ed3e906122859030908690600401616157565b60206040518083038186803b15801561229d57600080fd5b505afa1580156122b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506122d59190810190615cad565b901c6122f7576122f76001600160a01b0383168260001963ffffffff61293716565b5050565b60006010856001600160a01b03166354fd4d506040518163ffffffff1660e01b815260040160206040518083038186803b15801561233857600080fd5b505afa15801561234c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506123709190810190615c8f565b61ffff1610612409576040516301bafcdb60e61b81526001600160a01b03861690636ebf36c0906123b0908790879087906000908190819060040161626d565b602060405180830381600087803b1580156123ca57600080fd5b505af11580156123de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506124029190810190615cad565b9050612177565b6040516375892cf160e01b81526001600160a01b038616906375892cf19061243c90879087908790600090600401616238565b602060405180830381600087803b15801561245657600080fd5b505af115801561246a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061248e9190810190615cad565b95945050505050565b836001600160a01b0316856001600160a01b031614156124b657611284565b6124c9816208000063ffffffff61179f16565b61274a576006546001600160a01b03868116911614156125da576006546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a082319061251b903090600401616149565b60206040518083038186803b15801561253357600080fd5b505afa158015612547573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061256b9190810190615cad565b6040518263ffffffff1660e01b815260040161258791906164ab565b600060405180830381600087803b1580156125a157600080fd5b505af11580156125b5573d6000803e3d6000fd5b5050505061158c73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee85858585612a0c565b6007546001600160a01b0386811691161415612628576007546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a082319061251b903090600401616149565b6006546001600160a01b03858116911614156126c35761265f8573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585612497565b600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b1580156126af57600080fd5b505af1158015611b1e573d6000803e3d6000fd5b6007546001600160a01b038581169116141561274a576126fa8573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585612497565b600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b1580156126af57600080fd5b6112848585858585612a0c565b60606000826001600160a01b03166371f52bf36040518163ffffffff1660e01b815260040160206040518083038186803b15801561279457600080fd5b505afa1580156127a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127cc9190810190615cad565b60405190808252806020026020018201604052801561280557816020015b6127f2615772565b8152602001906001900390816127ea5790505b50915060005b8251811015612931576040516319b6401560e01b81526001600160a01b038516906319b64015906128409084906004016164ab565b60206040518083038186803b15801561285857600080fd5b505afa15801561286c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506128909190810190615ad6565b83828151811061289c57fe5b6020026020010151600001906001600160a01b031690816001600160a01b0316815250506128e1848483815181106128d057fe5b602002602001015160000151612a19565b8382815181106128ed57fe5b6020026020010151602001818152505061292783828151811061290c57fe5b60200260200101516020015183612b5090919063ffffffff16565b915060010161280b565b50915091565b6129408361111e565b611652576000811180156129d25750604051636eb1769f60e11b81526000906001600160a01b0385169063dd62ed3e90612980903090879060040161618d565b60206040518083038186803b15801561299857600080fd5b505afa1580156129ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506129d09190810190615cad565b115b156129f2576129f26001600160a01b03841683600063ffffffff612b7516565b6116526001600160a01b038416838363ffffffff612b7516565b6112848585858585612c3b565b60006016836001600160a01b03166354fd4d506040518163ffffffff1660e01b815260040160206040518083038186803b158015612a5657600080fd5b505afa158015612a6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612a8e9190810190615c8f565b61ffff1610612ac3576040516316095a2360e11b81526001600160a01b03841690632c12b446906112d890859060040161620f565b60405163359af54960e21b81526001600160a01b0384169063d66bd52490612aef90859060040161613b565b60a06040518083038186803b158015612b0757600080fd5b505afa158015612b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b3f9190810190615c71565b6020015163ffffffff169392505050565b6000828201838110156113285760405162461bcd60e51b8152600401610ff69061640e565b801580612bfd5750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e90612bab903090869060040161618d565b60206040518083038186803b158015612bc357600080fd5b505afa158015612bd7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612bfb9190810190615cad565b155b612c195760405162461bcd60e51b8152600401610ff69061648b565b60405161165290849063095ea7b360e01b9061148e90869086906024016161c3565b836001600160a01b0316856001600160a01b03161415612c5a57611284565b612c62615789565b612c6a613036565b9050612c7e8261080063ffffffff61179f16565b6130215760005b600a811015612e43578181600a8110612c9a57fe5b60200201516001600160a01b0316876001600160a01b03161415612e3b5760008282600a8110612cc657fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015612d0357600080fd5b505afa158015612d17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612d3b9190810190615ad6565b90508282600a8110612d4957fe5b60200201516001600160a01b0316632e1a7d4d876040518263ffffffff1660e01b8152600401612d7991906164ab565b600060405180830381600087803b158015612d9357600080fd5b505af1158015612da7573d6000803e3d6000fd5b50505050612e338188836001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401612ddc9190616149565b60206040518083038186803b158015612df457600080fd5b505afa158015612e08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e2c9190810190615cad565b8888612c3b565b505050611284565b600101612c85565b5060005b600a81101561301f578181600a8110612e5c57fe5b60200201516001600160a01b0316866001600160a01b031614156130175760008282600a8110612e8857fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015612ec557600080fd5b505afa158015612ed9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612efd9190810190615ad6565b9050612f0c8882888888613152565b612f26818484600a8110612f1c57fe5b602002015161223e565b8282600a8110612f3257fe5b60200201516001600160a01b031663b6b55f25826001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401612f719190616149565b60206040518083038186803b158015612f8957600080fd5b505afa158015612f9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612fc19190810190615cad565b6040518263ffffffff1660e01b8152600401612fdd91906164ab565b600060405180830381600087803b158015612ff757600080fd5b505af115801561300b573d6000803e3d6000fd5b50505050505050611284565b600101612e47565b505b61302e8686868686613152565b505050505050565b61303e615789565b5060408051610140810182527316de59092dae5ccf4a1e6439d611fd0653f0bd0181527304aa51bbcb46541455ccf1b8bef2ebc5d3787ec960208201527373a052500105205d34daf004eab301916da8190f918101919091527383f798e925bcd4017eb265844fddabb448f1707d606082015273d6ad7a6750a7593e092a9b218d66c0a814a3436e608082015273f61718057901f84c4eec4339ef8f0d86d2b4560060a08201527304bc0ab673d88ae9dbc9da2380cb6b79c4bca9ae60c082015273c2cb1040220768554cf699b0d863a3cd4324ce3260e082015273e6354ed5bc4b393a5aad09f21c46e101e692d4476101008201527326ea744e5b887e5205727f55dfbe8685e3b2195161012082015290565b61128485858585855b836001600160a01b0316856001600160a01b0316141561317a57611284565b61318b81601063ffffffff61179f16565b6133ad57613198856133ba565b1561325b5760006131a8866134b8565b60405163db006a7560e01b81529091506001600160a01b0387169063db006a75906131d79087906004016164ab565b602060405180830381600087803b1580156131f157600080fd5b505af1158015613205573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506132299190810190615cad565b5060006132456001600160a01b0383163063ffffffff61128b16565b9050613254828783878761315b565b5050611284565b613264846133ba565b156133ad576000613274856134b8565b9050613283868286868661358d565b600061329e6001600160a01b0383163063ffffffff61128b16565b90506132b2826001600160a01b031661111e565b1561332557601760009054906101000a90046001600160a01b03166001600160a01b0316631249c58b826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561330757600080fd5b505af115801561331b573d6000803e3d6000fd5b5050505050613254565b61332f828761223e565b60405163140e25ad60e31b81526001600160a01b0387169063a0712d689061335b9084906004016164ab565b602060405180830381600087803b15801561337557600080fd5b505af1158015613389573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e339190810190615cad565b611284858585858561358d565b6017546000906001600160a01b03838116911614156133db57506001610c2d565b6016546040516000916060916001600160a01b039091169061138890638e8f294b60e01b9061340e90889060240161620f565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161344c919061612f565b6000604051808303818686fa925050503d8060008114613488576040519150601f19603f3d011682016040523d82523d6000602084013e61348d565b606091505b5091509150816134a257600092505050610c2d565b6000818060200190516110e29190810190615a7e565b6017546000906001600160a01b03838116911614156134d957506000610c2d565b60408051600481526024810182526020810180516001600160e01b0316636f307dc360e01b17905290516000916060916001600160a01b0386169161138891613522919061612f565b6000604051808303818686fa925050503d806000811461355e576040519150601f19603f3d011682016040523d82523d6000602084013e613563565b606091505b5091509150816135795760001992505050610c2d565b808060200190516121779190810190615ad6565b6112848585858585836001600160a01b0316856001600160a01b031614156135b457611284565b6135c581602063ffffffff61179f16565b61384b5760006135d486610949565b90506001600160a01b038082161461372f576135f8816001600160a01b031661111e565b156136835760405163081a6b2560e41b81526001600160a01b038716906381a6b2509061362b9030908890600401616172565b602060405180830381600087803b15801561364557600080fd5b505af1158015613659573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061367d9190810190615cad565b50613705565b604051632770a7eb60e21b81526001600160a01b03871690639dc29fac906136b19030908890600401616172565b602060405180830381600087803b1580156136cb57600080fd5b505af11580156136df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506137039190810190615cad565b505b60006137206001600160a01b0383163063ffffffff61128b16565b90506132548287838787613854565b61373885610949565b90506001600160a01b0380821614613849576137578682868686613854565b60006137726001600160a01b0383163063ffffffff61128b16565b9050613786826001600160a01b031661111e565b1561381157604051638f6ede1f60e01b81526001600160a01b03871690638f6ede1f9083906137b9903090600401616149565b6020604051808303818588803b1580156137d257600080fd5b505af11580156137e6573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525061380b9190810190615cad565b50613254565b61381b828761223e565b6040516340c10f1960e01b81526001600160a01b038716906340c10f199061335b9030908590600401616172565b505b61128485858585855b61128485858585855b836001600160a01b0316856001600160a01b0316141561387c57611284565b61388d81608063ffffffff61179f16565b613a8f57600061389c86610cc0565b90506001600160a01b038082161461391f5760405163db006a7560e01b81526001600160a01b0387169063db006a75906138da9087906004016164ab565b600060405180830381600087803b1580156138f457600080fd5b505af1158015613908573d6000803e3d6000fd5b50505050613919818686868661385d565b50611284565b61392885610cc0565b90506001600160a01b0380821614613a8d576139478682868686613a98565b60006139626001600160a01b0383163063ffffffff61128b16565b90506139f382601560009054906101000a90046001600160a01b03166001600160a01b031663f2f4eb266040518163ffffffff1660e01b815260040160206040518083038186803b1580156139b657600080fd5b505afa1580156139ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506139ee9190810190615a0d565b61223e565b6015546001600160a01b039081169063d2d0e06690613a1390851661111e565b613a1e576000613a20565b825b613a32856001600160a01b031661111e565b613a3c5784613a52565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b8461044d6040518563ffffffff1660e01b8152600401613a74939291906163ac565b6000604051808303818588803b158015612ff757600080fd5b505b61128485858585855b836001600160a01b0316856001600160a01b03161415613ab757611284565b613ac98161040063ffffffff61179f16565b613d4f576013546001600160a01b0386811691161415613c2557601354604051637f8661a160e01b81526001600160a01b0390911690637f8661a190613b139086906004016164ab565b600060405180830381600087803b158015613b2d57600080fd5b505af1158015613b41573d6000803e3d6000fd5b50506014546040516370a0823160e01b8152600093506001600160a01b0390911691506370a0823190613b78903090600401616149565b60206040518083038186803b158015613b9057600080fd5b505afa158015613ba4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613bc89190810190615cad565b90508015613c0c57601454606090613bed906001600160a01b03168784600187610818565b601454909250613c0a91506001600160a01b031687848487611258565b505b600054613919906001600160a01b031686868686613d58565b6013546001600160a01b0385811691161415613d4f57600054613c559086906001600160a01b0316858585613d58565b600054601354613c71916001600160a01b03908116911661223e565b6013546000546040516370a0823160e01b81526001600160a01b039283169263049878f39216906370a0823190613cac903090600401616149565b60206040518083038186803b158015613cc457600080fd5b505afa158015613cd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613cfc9190810190615cad565b6040518263ffffffff1660e01b8152600401613d1891906164ab565b600060405180830381600087803b158015613d3257600080fd5b505af1158015613d46573d6000803e3d6000fd5b50505050611284565b61128485858585855b836001600160a01b0316856001600160a01b03161415613d7757611284565b613d8881604063ffffffff61179f16565b613f8c576012546001600160a01b0386811691161415613e955760125460405163ef693bed60e01b81526001600160a01b039091169063ef693bed90613dd49030908790600401616172565b600060405180830381600087803b158015613dee57600080fd5b505af1158015613e02573d6000803e3d6000fd5b50506000546040516370a0823160e01b815261158c93506001600160a01b039091169150869082906370a0823190613e3e903090600401616149565b60206040518083038186803b158015613e5657600080fd5b505afa158015613e6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613e8e9190810190615cad565b8585613f95565b6012546001600160a01b0385811691161415613f8c57600054613ec59086906001600160a01b0316858585613f95565b600054601254613ee1916001600160a01b03908116911661223e565b6012546000546040516370a0823160e01b81526001600160a01b0392831692633b4da69f9230929116906370a0823190613f1f908490600401616149565b60206040518083038186803b158015613f3757600080fd5b505afa158015613f4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613f6f9190810190615cad565b6040518363ffffffff1660e01b8152600401613d18929190616172565b61128485858585855b613fa7856001600160a01b031661111e565b158015613fc35750613fc1846001600160a01b031661111e565b155b8015613fdc5750613fdc8161020063ffffffff61179f16565b156140dd576060825160405190808252806020026020018201604052801561400e578160200160208202803883390190505b50905060005b83518110156140535783818151811061402957fe5b602002602001015160ff1682828151811061404057fe5b6020908102919091010152600101614014565b506140758673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8684866143e7565b60005b83518110156140bb57600884828151811061408f57fe5b6020026020010151901c60ff168282815181106140a857fe5b6020908102919091010152600101614078565b5061391973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee864784866143e7565b6000546001600160a01b0386811691161480159061410957506000546001600160a01b03858116911614155b80156141235750614123816201000063ffffffff61179f16565b156142895760608251604051908082528060200260200182016040528015614155578160200160208202803883390190505b50905060005b835181101561419a5783818151811061417057fe5b602002602001015160ff1682828151811061418757fe5b602090810291909101015260010161415b565b506000546141b59087906001600160a01b03168684866143e7565b60005b83518110156141fb5760088482815181106141cf57fe5b6020026020010151901c60ff168282815181106141e857fe5b60209081029190910101526001016141b8565b506000546040516370a0823160e01b8152613919916001600160a01b031690879082906370a0823190614232903090600401616149565b60206040518083038186803b15801561424a57600080fd5b505afa15801561425e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506142829190810190615cad565b84866143e7565b6001546001600160a01b038681169116148015906142b557506001546001600160a01b03858116911614155b80156142cf57506142cf816202000063ffffffff61179f16565b156143de5760608251604051908082528060200260200182016040528015614301578160200160208202803883390190505b50905060005b83518110156143465783818151811061431c57fe5b602002602001015160ff1682828151811061433357fe5b6020908102919091010152600101614307565b506001546143619087906001600160a01b03168684866143e7565b60005b83518110156143a757600884828151811061437b57fe5b6020026020010151901c60ff1682828151811061439457fe5b6020908102919091010152600101614364565b506001546040516370a0823160e01b8152613919916001600160a01b031690879082906370a0823190614232903090600401616149565b61128485858585855b836001600160a01b0316856001600160a01b0316141561440657611284565b61440e6157a8565b50604080516101208101825261458381526147f7602082015261493491810191909152614cc16060820152614f50608082015261509760a08201526151e660c082015261538860e082015261552a610100820152600080805b60098110156144c057600086828151811061447e57fe5b602002602001015111156144b8576144b286828151811061449b57fe5b602002602001015184612b5090919063ffffffff16565b92508091505b600101614467565b50600082116144e15760405162461bcd60e51b8152600401610ff69061643e565b8560005b6009811015614577578681815181106144fa57fe5b60200260200101516000141561450f5761456f565b600061453b856110ae8a858151811061452457fe5b60200260200101518c6113ac90919063ffffffff16565b9050838214156145485750815b808303925061456c8b8b8389866009811061455f57fe5b602002015163ffffffff16565b50505b6001016144e5565b50505050505050505050565b6000816145986001600160a01b03861661111e565b6146c0576009546040516303795fb160e11b81526000916001600160a01b0316906306f2bf62906145cd90899060040161620f565b60206040518083038186803b1580156145e557600080fd5b505afa1580156145f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061461d9190810190615ad6565b90506001600160a01b038116156146be57614638868261223e565b6040516395e3c50b60e01b81526001600160a01b038216906395e3c50b9061466990859060019042906004016164d9565b602060405180830381600087803b15801561468357600080fd5b505af1158015614697573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506146bb9190810190615cad565b91505b505b6146d2846001600160a01b031661111e565b612177576009546040516303795fb160e11b81526000916001600160a01b0316906306f2bf629061470790889060040161620f565b60206040518083038186803b15801561471f57600080fd5b505afa158015614733573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506147579190810190615ad6565b90506001600160a01b038116156147ee5760405163f39b5b9b60e01b81526001600160a01b0382169063f39b5b9b9084906147999060019042906004016163ef565b6020604051808303818588803b1580156147b257600080fd5b505af11580156147c6573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506147eb9190810190615cad565b91505b50949350505050565b6008546000906148119085906001600160a01b031661223e565b6008546001600160a01b03908116906329589f619061483190871661111e565b61483c57600061483e565b835b614850876001600160a01b031661111e565b61485a5786614870565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b85614883886001600160a01b031661111e565b61488d57876148a3565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b30600160ff1b6000734d37f28d2db99e8d35a6c725a5f1749a085850a36040518963ffffffff1660e01b81526004016148e29796959493929190616309565b6020604051808303818588803b1580156148fb57600080fd5b505af115801561490f573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506121779190810190615cad565b6000614948846001600160a01b031661111e565b156149b757600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561499d57600080fd5b505af11580156149b1573d6000803e3d6000fd5b50505050505b600a54604051632ecd14d360e21b81526000916001600160a01b03169063bb34534c906149e69060040161647e565b60206040518083038186803b1580156149fe57600080fd5b505afa158015614a12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614a369190810190615a0d565b600b549091506060906001600160a01b03908116906375e1cc8290614a5c90891661111e565b614a665787614a73565b6007546001600160a01b03165b614a85886001600160a01b031661111e565b614a8f5787614a9c565b6007546001600160a01b03165b6040518363ffffffff1660e01b8152600401614ab992919061621d565b60006040518083038186803b158015614ad157600080fd5b505afa158015614ae5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052614b0d9190810190615a2b565b9050614b41614b24876001600160a01b031661111e565b614b2e5786614b3b565b6007546001600160a01b03165b8361223e565b6040516331ee892f60e21b81526000906001600160a01b0384169063c7ba24bc90614b7590859089906001906004016161d1565b602060405180830381600087803b158015614b8f57600080fd5b505af1158015614ba3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614bc79190810190615cad565b9050614bdb866001600160a01b031661111e565b15614cb7576007546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a0823190614c18903090600401616149565b60206040518083038186803b158015614c3057600080fd5b505afa158015614c44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614c689190810190615cad565b6040518263ffffffff1660e01b8152600401614c8491906164ab565b600060405180830381600087803b158015614c9e57600080fd5b505af1158015614cb2573d6000803e3d6000fd5b505050505b9695505050505050565b6000614cd5846001600160a01b031661111e565b15614d4457600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015614d2a57600080fd5b505af1158015614d3e573d6000803e3d6000fd5b50505050505b614d81614d59856001600160a01b031661111e565b614d635784614d70565b6006546001600160a01b03165b600c546001600160a01b031661223e565b600c546000906001600160a01b0390811690630621b4f690614da490881661111e565b614dae5786614dbb565b6006546001600160a01b03165b85614dce886001600160a01b031661111e565b614dd85787614de5565b6006546001600160a01b03165b60016040518563ffffffff1660e01b8152600401614e069493929190616384565b602060405180830381600087803b158015614e2057600080fd5b505af1158015614e34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614e589190810190615cad565b9050614e6c846001600160a01b031661111e565b15612177576006546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a0823190614ea9903090600401616149565b60206040518083038186803b158015614ec157600080fd5b505afa158015614ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614ef99190810190615cad565b6040518263ffffffff1660e01b8152600401614f1591906164ab565b600060405180830381600087803b158015614f2f57600080fd5b505af1158015614f43573d6000803e3d6000fd5b5050505090509392505050565b60015460009081906001600160a01b03868116911614614f71576000614f74565b60025b6000546001600160a01b03878116911614614f90576000614f93565b60015b600154910160ff1691506000906001600160a01b0390811690861614614fba576000614fbd565b60025b6000546001600160a01b03878116911614614fd9576000614fdc565b60015b0160ff16905081600f0b60001480614ff7575080600f0b6000145b1561500757600092505050611328565b600d5461501e9087906001600160a01b031661223e565b600d54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b600060405180830381600087803b15801561507657600080fd5b505af115801561508a573d6000803e3d6000fd5b5050505050509392505050565b60025460009081906001600160a01b038681169116146150b85760006150bb565b60035b6001546001600160a01b038781169116146150d75760006150da565b60025b6000546001600160a01b038881169116146150f65760006150f9565b60015b60025491019190910160ff1691506000906001600160a01b0390811690861614615124576000615127565b60035b6001546001600160a01b03878116911614615143576000615146565b60025b6000546001600160a01b03888116911614615162576000615165565b60015b010160ff16905081600f0b60001480615181575080600f0b6000145b1561519157600092505050611328565b600e546151a89087906001600160a01b031661223e565b600e54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b60035460009081906001600160a01b0386811691161461520757600061520a565b60045b6002546001600160a01b03878116911614615226576000615229565b60035b6001546001600160a01b03888116911614615245576000615248565b60025b6000546001600160a01b03898116911614615264576000615267565b60015b01010160ff1690506000600360009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b0316146152a65760006152a9565b60045b6002546001600160a01b038781169116146152c55760006152c8565b60035b6001546001600160a01b038881169116146152e45760006152e7565b60025b6000546001600160a01b03898116911614615303576000615306565b60015b01010160ff16905081600f0b60001480615323575080600f0b6000145b1561533357600092505050611328565b600f5461534a9087906001600160a01b031661223e565b600f54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b60045460009081906001600160a01b038681169116146153a95760006153ac565b60045b6002546001600160a01b038781169116146153c85760006153cb565b60035b6001546001600160a01b038881169116146153e75760006153ea565b60025b6000546001600160a01b03898116911614615406576000615409565b60015b01010160ff1690506000600460009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b03161461544857600061544b565b60045b6002546001600160a01b0387811691161461546757600061546a565b60035b6001546001600160a01b03888116911614615486576000615489565b60025b6000546001600160a01b038981169116146154a55760006154a8565b60015b01010160ff16905081600f0b600014806154c5575080600f0b6000145b156154d557600092505050611328565b6010546154ec9087906001600160a01b031661223e565b601054604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b60055460009081906001600160a01b0386811691161461554b57600061554e565b60055b6003546001600160a01b0387811691161461556a57600061556d565b60045b6002546001600160a01b0388811691161461558957600061558c565b60035b6001546001600160a01b038981169116146155a85760006155ab565b60025b6000546001600160a01b038a81169116146155c75760006155ca565b60015b0101010160ff1690506000600560009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b03161461560a57600061560d565b60055b6003546001600160a01b0387811691161461562957600061562c565b60045b6002546001600160a01b0388811691161461564857600061564b565b60035b6001546001600160a01b0389811691161461566757600061566a565b60025b6000546001600160a01b038a8116911614615686576000615689565b60015b0101010160ff16905081600f0b600014806156a7575080600f0b6000145b156156b757600092505050611328565b6005546001600160a01b038781169116148015906156e357506005546001600160a01b03868116911614155b156156f357600092505050611328565b60115461570a9087906001600160a01b031661223e565b601154604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b60405180606001604052806060815260200160006001600160a01b03168152602001600081525090565b604080518082019091526000808252602082015290565b604051806101400160405280600a906020820280388339509192915050565b6040518061012001604052806009905b6157d18152602001906001900390816157b85790505090565bfe5b8051611152816165e6565b600082601f8301126157ef57600080fd5b81516158026157fd8261651b565b6164f4565b9150818183526020840193506020810190508385602084028201111561582757600080fd5b60005b83811015615853578161583d88826157d3565b845250602092830192919091019060010161582a565b5050505092915050565b600082601f83011261586e57600080fd5b813561587c6157fd8261651b565b915081818352602084019350602081019050838560208402820111156158a157600080fd5b60005b8381101561585357816158b788826159ec565b84525060209283019291909101906001016158a4565b600082601f8301126158de57600080fd5b81516158ec6157fd8261651b565b9150818183526020840193506020810190508385602084028201111561591157600080fd5b60005b83811015615853578161592788826159f7565b8452506020928301929190910190600101615914565b8051611152816165fd565b803561115281616606565b805161115281616606565b600060a0828403121561597057600080fd5b61597a60a06164f4565b9050600061598884846159f7565b825250602061599984848301615a02565b60208301525060406159ad8482850161593d565b60408301525060606159c18482850161593d565b60608301525060806159d58482850161593d565b60808301525092915050565b80516111528161660f565b803561115281616618565b805161115281616618565b805161115281616621565b600060208284031215615a1f57600080fd5b600061217784846157d3565b600060208284031215615a3d57600080fd5b815167ffffffffffffffff811115615a5457600080fd5b612177848285016157de565b600060208284031215615a7257600080fd5b6000612177848461593d565b60008060408385031215615a9157600080fd5b6000615a9d858561593d565b9250506020615aae858286016159f7565b9150509250929050565b600060208284031215615aca57600080fd5b60006121778484615948565b600060208284031215615ae857600080fd5b60006121778484615953565b60008060008060008060c08789031215615b0d57600080fd5b6000615b198989615948565b9650506020615b2a89828a01615948565b9550506040615b3b89828a016159ec565b9450506060615b4c89828a016159ec565b935050608087013567ffffffffffffffff811115615b6957600080fd5b615b7589828a0161585d565b92505060a0615b8689828a016159ec565b9150509295509295509295565b600080600080600060a08688031215615bab57600080fd5b6000615bb78888615948565b9550506020615bc888828901615948565b9450506040615bd9888289016159ec565b9350506060615bea888289016159ec565b9250506080615bfb888289016159ec565b9150509295509295909350565b60008060008060008060c08789031215615c2157600080fd5b6000615c2d8989615948565b9650506020615c3e89828a01615948565b9550506040615c4f89828a016159ec565b9450506060615c6089828a016159ec565b9350506080615b7589828a016159ec565b600060a08284031215615c8357600080fd5b6000612177848461595e565b600060208284031215615ca157600080fd5b600061217784846159e1565b600060208284031215615cbf57600080fd5b600061217784846159f7565b60008060408385031215615cde57600080fd5b6000615cea85856159f7565b925050602083015167ffffffffffffffff811115615d0757600080fd5b615aae858286016158cd565b6000615d1f8383615d42565b505060200190565b6000615d1f8383616126565b615d3c8161658f565b82525050565b615d3c8161654f565b6000615d5682616542565b615d608185616546565b9350615d6b8361653c565b8060005b83811015615d99578151615d838882615d13565b9750615d8e8361653c565b925050600101615d6f565b509495945050505050565b6000615daf82616542565b615db98185616546565b9350615dc48361653c565b8060005b83811015615d99578151615ddc8882615d27565b9750615de78361653c565b925050600101615dc8565b6000615dfd82616542565b615e078185610c2d565b9350615e178185602086016165b0565b9290920192915050565b615d3c8161655f565b615d3c8161656a565b615d3c8161659a565b615d3c816165a5565b6000615e5082616542565b615e5a8185616546565b9350615e6a8185602086016165b0565b615e73816165dc565b9093019392505050565b6000615e8a601b83616546565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b6000615ec3603583616546565b7f4f6e6553706c69743a2061637475616c2072657475726e20616d6f756e74206981527439903632b9b9903a3430b71036b4b72932ba3ab93760591b602082015260400192915050565b6000615f1a602083616546565b7f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815260200192915050565b6000615f53602f83616546565b7f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f81526e6e7461696e206e6f6e2d7a65726f7360881b602082015260400192915050565b6000615fa4602b83616546565b7f57726f6e6720757365616765206f66204554482e756e6976657273616c54726181526a6e7366657246726f6d282960a81b602082015260400192915050565b6000615ff1602183616546565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000611152600083616546565b6000616041602a83616546565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e8152691bdd081cdd58d8d9595960b21b602082015260400192915050565b6c42616e636f724e6574776f726b60981b9052565b60006160a2603683616546565b7f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f81527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b602082015260400192915050565b60006160fa601f83616546565b7f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400815260200192915050565b615d3c81616583565b60006113288284615df2565b602081016111528284615d42565b602081016111528284615d33565b604081016161658285615d33565b6113286020830184615d42565b604081016161808285615d33565b6113286020830184616126565b604081016161658285615d42565b606081016161a98286615d42565b6161b66020830185615d42565b6121776040830184616126565b604081016161808285615d42565b606080825281016161e28186615d4b565b90506161f16020830185616126565b6121776040830184615e33565b602080825281016113288184615da4565b602081016111528284615e21565b6040810161622b8285615e21565b6113286020830184615e21565b608081016162468287615e21565b6162536020830186615e21565b6162606040830185616126565b61248e6060830184615e33565b60c0810161627b8289615e21565b6162886020830188615e21565b6162956040830187616126565b6162a26060830186615e33565b6162af6080830185615d33565b6162bc60a0830184615e33565b979650505050505050565b60a081016162d58288615e21565b6162e26020830187615e21565b6162ef6040830186616126565b6162fc6060830185616126565b614cb76080830184616126565b6101008101616318828a615e21565b6163256020830189616126565b6163326040830188615e21565b61633f6060830187615d33565b61634c6080830186615e33565b61635960a0830185615e33565b61636660c0830184615d33565b81810360e083015261637781616027565b9998505050505050505050565b608081016163928287615e21565b61639f6020830186616126565b6162606040830185615e21565b606081016163ba8286615e21565b6163c76020830185616126565b6121776040830184615e3c565b608081016163e28287615e2a565b6162536020830186615e2a565b604081016161808285615e33565b602080825281016113288184615e45565b6020808252810161115281615e7d565b6020808252810161115281615eb6565b6020808252810161115281615f0d565b6020808252810161115281615f46565b6020808252810161115281615f97565b6020808252810161115281615fe4565b6020808252810161115281616034565b60208101610c2d82616080565b6020808252810161115281616095565b60208082528101611152816160ed565b602081016111528284616126565b604081016164c78285616126565b81810360208301526121778184615da4565b606081016164e78286616126565b6161b66020830185615e33565b60405181810167ffffffffffffffff8111828210171561651357600080fd5b604052919050565b600067ffffffffffffffff82111561653257600080fd5b5060209081020190565b60200190565b5190565b90815260200190565b600061115282616577565b151590565b60006111528261654f565b600f0b90565b61ffff1690565b6001600160a01b031690565b90565b63ffffffff1690565b60006111528261655f565b600061115282616583565b600061115282616570565b60005b838110156165cb5781810151838201526020016165b3565b838111156112525750506000910152565b601f01601f191690565b6165ef8161654f565b81146165fa57600080fd5b50565b6165ef8161655a565b6165ef8161655f565b6165ef81616570565b6165ef81616583565b6165ef8161658656fea365627a7a723158208b80610e5ccc7644892fc0a33e2a10b10d27b8f595dfa293f6f52c6a14cce8f66c6578706572696d656e74616cf564736f6c63430005110040 \ No newline at end of file diff --git a/OneSplit.full.sol b/OneSplit.full.sol index 518583a..210ef89 100644 --- a/OneSplit.full.sol +++ b/OneSplit.full.sol @@ -3089,7 +3089,8 @@ contract OneSplitWeth is OneSplitBase { // File: contracts/interface/ISmartTokenConverter.sol -pragma solidity ^0.5.0;pragma experimental ABIEncoderV2; +pragma solidity ^0.5.0; +//pragma experimental ABIEncoderV2; interface ISmartTokenConverter { @@ -3168,7 +3169,7 @@ interface ISmartTokenFormula { // File: contracts/OneSplitSmartToken.sol pragma solidity ^0.5.0; -pragma experimental ABIEncoderV2; +//pragma experimental ABIEncoderV2; @@ -3498,7 +3499,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { return super._swap( smartTokenDetails.reserveTokenList[i].token, toToken, - tokenBalanceAfter.sub(tokenBalanceBefore[i]), + tokenBalanceAfter.sub(tokenBalanceBefore[i]), dist[i], disableFlags ); @@ -3666,8 +3667,8 @@ contract OneSplit is OneSplitFulcrum, OneSplitCompound, OneSplitIearn, - OneSplitWeth - //OneSplitSmartToken + OneSplitWeth, + OneSplitSmartToken { IOneSplitView public oneSplitView; diff --git a/contracts/OneSplit.sol b/contracts/OneSplit.sol index 283efaa..da1cc0a 100644 --- a/contracts/OneSplit.sol +++ b/contracts/OneSplit.sol @@ -66,8 +66,8 @@ contract OneSplit is OneSplitFulcrum, OneSplitCompound, OneSplitIearn, - OneSplitWeth - //OneSplitSmartToken + OneSplitWeth, + OneSplitSmartToken { IOneSplitView public oneSplitView; From 323ef6b5923f1e7bcd180aa0381b78ca927182c6 Mon Sep 17 00:00:00 2001 From: Kirill Date: Wed, 1 Apr 2020 22:06:51 +0300 Subject: [PATCH 07/60] fix msg.sender -> address(this) --- contracts/OneSplitSmartToken.sol | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index c93af38..bd3fbdf 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -315,7 +315,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { dist[i] = new uint256[](distribution.length); - tokenBalanceBefore[i] = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + tokenBalanceBefore[i] = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); for (uint j = 0; j < distribution.length; j++) { dist[i][j] = (distribution[j] >> (i * 8)) & 0xFF; @@ -325,7 +325,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { ISmartTokenConverter(smartTokenDetails.converter).liquidate(amount); for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { - uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); return super._swap( smartTokenDetails.reserveTokenList[i].token, @@ -350,6 +350,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(toToken))); + uint256[] memory dist = new uint256[](distribution.length); uint256[] memory fundAmounts = new uint256[](smartTokenDetails.reserveTokenList.length); for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { @@ -359,21 +360,21 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { smartTokenDetails.totalReserveTokensRatio ); - uint256 tokenBalanceBefore = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + uint256 tokenBalanceBefore = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); for (uint j = 0; j < distribution.length; j++) { - distribution[j] = (distribution[j] >> (i * 8)) & 0xFF; + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; } super._swap( fromToken, smartTokenDetails.reserveTokenList[i].token, exchangeAmount, - distribution, + dist, disableFlags ); - uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); fundAmounts[i] = toToken.totalSupply() .mul(tokenBalanceAfter.sub(tokenBalanceBefore)) From 536b05d45ed72d53a56ef89055e6502e2b4bd7d5 Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 2 Apr 2020 00:03:52 +0300 Subject: [PATCH 08/60] fix minReturnAmount --- contracts/OneSplitSmartToken.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index bd3fbdf..39b1760 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -422,10 +422,10 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { { // todo: think about minReturn, affiliateAccount, affiliateFee if (converter.version() >= 16) { - return converter.convert2(_fromToken, _toToken, _amount, 0, address(0), 0); + return converter.convert2(_fromToken, _toToken, _amount, 1, address(0), 0); } - return converter.convert(_fromToken, _toToken, _amount, 0); + return converter.convert(_fromToken, _toToken, _amount, 1); } } From 14888251f0e35e6493d0f9472f674ca449ee5098 Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 2 Apr 2020 01:38:08 +0300 Subject: [PATCH 09/60] complete some --- contracts/OneSplitSmartToken.sol | 168 ++++++++++++++++++++++++------- 1 file changed, 134 insertions(+), 34 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 39b1760..e053bd4 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -12,6 +12,7 @@ contract OneSplitSmartTokenBase { ISmartTokenRegistry smartTokenRegistry = ISmartTokenRegistry(0xf6E2D7F616B67E46D708e4410746E9AAb3a4C518); ISmartTokenFormula smartTokenFormula = ISmartTokenFormula(0x524619EB9b4cdFFa7DA13029b33f24635478AFc0); + IERC20 bntToken = IERC20(0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C); struct TokenWithRatio { IERC20 token; @@ -96,7 +97,46 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { if (!disableFlags.check(FLAG_DISABLE_SMART_TOKEN)) { - if (smartTokenRegistry.isSmartToken(fromToken)) { + bool isSmartTokenFrom = smartTokenRegistry.isSmartToken(fromToken); + bool isSmartTokenTo = smartTokenRegistry.isSmartToken(toToken); + + // todo: figure out with the correct calc of distribution + if (isSmartTokenFrom && isSmartTokenTo) { + + ( + uint256 returnBntAmount, + uint256[] memory smartTokenFromDistribution + ) = _getExpectedReturnFromSmartToken( + fromToken, + bntToken, + amount, + parts, + disableFlags + ); + + ( + uint256 returnSmartTokenToAmount, + uint256[] memory smartTokenToDistribution + ) = _getExpectedReturnToSmartToken( + bntToken, + toToken, + returnBntAmount, + parts, + disableFlags + ); + + uint256[] memory finalDistribution = new uint256[](smartTokenToDistribution.length); + for (uint i = 0; i < smartTokenToDistribution.length; i++) { + + + + } + + return (returnSmartTokenToAmount, finalDistribution); + + } + + if (isSmartTokenFrom) { return _getExpectedReturnFromSmartToken( fromToken, @@ -108,7 +148,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { } - if (smartTokenRegistry.isSmartToken(toToken)) { + if (isSmartTokenTo) { return _getExpectedReturnToSmartToken( fromToken, @@ -149,6 +189,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(fromToken))); for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( fromToken.totalSupply(), smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter), @@ -156,6 +197,11 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { amount ); + if (smartTokenDetails.reserveTokenList[i].token == toToken) { + returnAmount = returnAmount.add(srcAmount); + continue; + } + (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( smartTokenDetails.reserveTokenList[i].token, toToken, @@ -200,16 +246,22 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { smartTokenDetails.totalReserveTokensRatio ); - (uint256 tokenAmount, uint256[] memory dist) = super.getExpectedReturn( - fromToken, - smartTokenDetails.reserveTokenList[i].token, - exchangeAmount, - parts, - disableFlags | FLAG_DISABLE_BANCOR - ); + uint256 tokenAmount; + uint256[] memory dist; + if (smartTokenDetails.reserveTokenList[i].token != fromToken) { + (tokenAmount, dist) = super.getExpectedReturn( + fromToken, + smartTokenDetails.reserveTokenList[i].token, + exchangeAmount, + parts, + disableFlags | FLAG_DISABLE_BANCOR + ); - for (uint j = 0; j < distribution.length; j++) { - distribution[j] = distribution[j].add(dist[j] << (i * 8)); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] = distribution[j].add(dist[j] << (i * 8)); + } + } else { + tokenAmount = exchangeAmount; } fundAmounts[i] = toToken.totalSupply() @@ -249,10 +301,6 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { - // todo: think about smart tokens with one token in reserve - // todo: think about the case when toToken == reserveToken - // todo: think about the case when fromToken == reserveToken - // todo: think about the case when fromToken == smartToken1, toToken == smartToken2 function _swap( IERC20 fromToken, IERC20 toToken, @@ -260,13 +308,42 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { uint256[] memory distribution, uint256 disableFlags ) internal { + if (fromToken == toToken) { return; } if (!disableFlags.check(FLAG_DISABLE_SMART_TOKEN)) { - if (smartTokenRegistry.isSmartToken(fromToken)) { + bool isSmartTokenFrom = smartTokenRegistry.isSmartToken(fromToken); + bool isSmartTokenTo = smartTokenRegistry.isSmartToken(toToken); + + // todo: figure out with the correct calc of distribution + if (isSmartTokenFrom && isSmartTokenTo) { + + uint256 bntBalanceBefore = bntToken.balanceOf(address(this)); + + _swapFromSmartToken( + fromToken, + bntToken, + amount, + distribution, + disableFlags + ); + + uint256 bntBalanceAfter = bntToken.balanceOf(address(this)); + + return _swapToSmartToken( + bntToken, + toToken, + bntBalanceAfter.sub(bntBalanceBefore), + distribution, + disableFlags + ); + + } + + if (isSmartTokenFrom) { return _swapFromSmartToken( fromToken, @@ -278,7 +355,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { } - if (smartTokenRegistry.isSmartToken(toToken)) { + if (isSmartTokenTo) { return _swapToSmartToken( fromToken, @@ -313,6 +390,11 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { uint256[] memory tokenBalanceBefore = new uint256[](smartTokenDetails.reserveTokenList.length); uint256[][] memory dist = new uint256[][](smartTokenDetails.reserveTokenList.length); for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + + if (smartTokenDetails.reserveTokenList[i].token == toToken) { + continue; + } + dist[i] = new uint256[](distribution.length); tokenBalanceBefore[i] = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); @@ -320,20 +402,27 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { for (uint j = 0; j < distribution.length; j++) { dist[i][j] = (distribution[j] >> (i * 8)) & 0xFF; } + } ISmartTokenConverter(smartTokenDetails.converter).liquidate(amount); for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + + if (smartTokenDetails.reserveTokenList[i].token == toToken) { + continue; + } + uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); - return super._swap( + super._swap( smartTokenDetails.reserveTokenList[i].token, toToken, tokenBalanceAfter.sub(tokenBalanceBefore[i]), dist[i], disableFlags ); + } } @@ -360,25 +449,35 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { smartTokenDetails.totalReserveTokensRatio ); - uint256 tokenBalanceBefore = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); + if (smartTokenDetails.reserveTokenList[i].token != fromToken) { - for (uint j = 0; j < distribution.length; j++) { - dist[j] = (distribution[j] >> (i * 8)) & 0xFF; - } + uint256 tokenBalanceBefore = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); - super._swap( - fromToken, - smartTokenDetails.reserveTokenList[i].token, - exchangeAmount, - dist, - disableFlags - ); + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } - uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); + super._swap( + fromToken, + smartTokenDetails.reserveTokenList[i].token, + exchangeAmount, + dist, + disableFlags + ); - fundAmounts[i] = toToken.totalSupply() - .mul(tokenBalanceAfter.sub(tokenBalanceBefore)) - .div(smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter)); + uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); + + fundAmounts[i] = toToken.totalSupply() + .mul(tokenBalanceAfter.sub(tokenBalanceBefore)) + .div(smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter)); + + } else { + + fundAmounts[i] = toToken.totalSupply() + .mul(exchangeAmount) + .div(smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter)); + + } if (fundAmounts[i] < minFundAmount) { minFundAmount = fundAmounts[i]; @@ -391,6 +490,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { // Swap leftovers for SmartToken for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + uint256 reserveBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter); uint256 leftover = fundAmounts[i].sub(minFundAmount) @@ -407,6 +507,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { ); } + } } @@ -420,7 +521,6 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { private returns (uint256) { - // todo: think about minReturn, affiliateAccount, affiliateFee if (converter.version() >= 16) { return converter.convert2(_fromToken, _toToken, _amount, 1, address(0), 0); } From f728277d3894588a97fa626c37991532d13b128d Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 2 Apr 2020 19:21:59 +0300 Subject: [PATCH 10/60] edit distribution --- contracts/OneSplitSmartToken.sol | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index e053bd4..990cf86 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -125,14 +125,12 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { disableFlags ); - uint256[] memory finalDistribution = new uint256[](smartTokenToDistribution.length); for (uint i = 0; i < smartTokenToDistribution.length; i++) { - - - + smartTokenFromDistribution[i] += smartTokenFromDistribution[i] + .add(smartTokenToDistribution[i] << 128); } - return (returnSmartTokenToAmount, finalDistribution); + return (returnSmartTokenToAmount, smartTokenFromDistribution); } @@ -249,6 +247,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { uint256 tokenAmount; uint256[] memory dist; if (smartTokenDetails.reserveTokenList[i].token != fromToken) { + (tokenAmount, dist) = super.getExpectedReturn( fromToken, smartTokenDetails.reserveTokenList[i].token, @@ -260,8 +259,11 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { for (uint j = 0; j < distribution.length; j++) { distribution[j] = distribution[j].add(dist[j] << (i * 8)); } + } else { + tokenAmount = exchangeAmount; + } fundAmounts[i] = toToken.totalSupply() @@ -275,6 +277,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { // Swap leftovers for SmartToken for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + uint256 reserveBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter); uint256 leftover = fundAmounts[i].sub(minFundAmount) @@ -282,6 +285,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { .div(toToken.totalSupply()); if (leftover > 0) { + minFundAmount = minFundAmount.add( smartTokenFormula.calculatePurchaseReturn( toToken.totalSupply(), @@ -290,7 +294,9 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { leftover ) ); + } + } return (minFundAmount, distribution); @@ -321,23 +327,32 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { // todo: figure out with the correct calc of distribution if (isSmartTokenFrom && isSmartTokenTo) { + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] & ((1 << 128) - 1); + } + uint256 bntBalanceBefore = bntToken.balanceOf(address(this)); _swapFromSmartToken( fromToken, bntToken, amount, - distribution, + dist, disableFlags ); + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] >> 128; + } + uint256 bntBalanceAfter = bntToken.balanceOf(address(this)); return _swapToSmartToken( bntToken, toToken, bntBalanceAfter.sub(bntBalanceBefore), - distribution, + dist, disableFlags ); From 98a63686a91139b188d5858ae3bfbc52f8dd1011 Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 2 Apr 2020 22:41:51 +0300 Subject: [PATCH 11/60] reservers -> connectors + remove experimental encoder --- contracts/OneSplit.sol | 1 - contracts/OneSplitSmartToken.sol | 12 +++++++----- contracts/interface/ISmartTokenConverter.sol | 12 ++---------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/contracts/OneSplit.sol b/contracts/OneSplit.sol index da1cc0a..24f48ac 100644 --- a/contracts/OneSplit.sol +++ b/contracts/OneSplit.sol @@ -1,5 +1,4 @@ pragma solidity ^0.5.0; -pragma experimental ABIEncoderV2; import "./IOneSplit.sol"; import "./OneSplitBase.sol"; diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 990cf86..12808b6 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -1,5 +1,4 @@ pragma solidity ^0.5.0; -pragma experimental ABIEncoderV2; import "./interface/ISmartToken.sol"; import "./interface/ISmartTokenRegistry.sol"; @@ -60,11 +59,15 @@ contract OneSplitSmartTokenBase { view returns (uint256) { - if (converter.version() >= 22) { + + uint16 version = converter.version(); + if (version >= 22) { return converter.getReserveRatio(token); } - return uint256(converter.reserves(address(token)).ratio); + (, uint32 ratio, , ,) = converter.connectors(address(token)); + + return uint256(ratio); } function _calcExchangeAmount(uint256 amount, uint256 ratio, uint256 totalRatio) internal pure returns (uint256) { @@ -100,7 +103,6 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { bool isSmartTokenFrom = smartTokenRegistry.isSmartToken(fromToken); bool isSmartTokenTo = smartTokenRegistry.isSmartToken(toToken); - // todo: figure out with the correct calc of distribution if (isSmartTokenFrom && isSmartTokenTo) { ( @@ -324,7 +326,6 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { bool isSmartTokenFrom = smartTokenRegistry.isSmartToken(fromToken); bool isSmartTokenTo = smartTokenRegistry.isSmartToken(toToken); - // todo: figure out with the correct calc of distribution if (isSmartTokenFrom && isSmartTokenTo) { uint256[] memory dist = new uint256[](distribution.length); @@ -499,6 +500,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { } _infiniteApproveIfNeeded(smartTokenDetails.reserveTokenList[i].token, smartTokenDetails.converter); + } ISmartTokenConverter(smartTokenDetails.converter).fund(minFundAmount); diff --git a/contracts/interface/ISmartTokenConverter.sol b/contracts/interface/ISmartTokenConverter.sol index 8478411..02f4744 100644 --- a/contracts/interface/ISmartTokenConverter.sol +++ b/contracts/interface/ISmartTokenConverter.sol @@ -1,20 +1,12 @@ -pragma solidity ^0.5.0;pragma experimental ABIEncoderV2; +pragma solidity ^0.5.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface ISmartTokenConverter { - struct Reserve { - uint256 virtualBalance; // reserve virtual balance - uint32 ratio; // reserve ratio, represented in ppm, 1-1000000 - bool isVirtualBalanceEnabled; // true if virtual balance is enabled, false if not - bool isSaleEnabled; // is sale of the reserve token enabled, can be set by the owner - bool isSet; // used to tell if the mapping element is defined - } - function version() external view returns (uint16); - function reserves(address) external view returns (Reserve memory); + function connectors(address) external view returns (uint256, uint32, bool, bool, bool); function getReserveRatio(IERC20 token) external view returns (uint256); From dc408b54c2670fb6ab1ac1fcee6aa0bb1b3534f7 Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 3 Apr 2020 03:04:00 +0300 Subject: [PATCH 12/60] get version -> staticcall --- contracts/OneSplitSmartToken.sol | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 12808b6..5d6ccaf 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -60,9 +60,15 @@ contract OneSplitSmartTokenBase { returns (uint256) { - uint16 version = converter.version(); - if (version >= 22) { - return converter.getReserveRatio(token); + (bool ok, bytes memory data) = address(converter).staticcall.gas(10000)( + abi.encodeWithSelector( + converter.getReserveRatio.selector, + token + ) + ); + + if (ok) { + return abi.decode(data, (uint256)); } (, uint32 ratio, , ,) = converter.connectors(address(token)); @@ -275,6 +281,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { if (fundAmounts[i] < minFundAmount) { minFundAmount = fundAmounts[i]; } + } // Swap leftovers for SmartToken @@ -478,7 +485,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { smartTokenDetails.reserveTokenList[i].token, exchangeAmount, dist, - disableFlags + disableFlags | FLAG_DISABLE_BANCOR ); uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); @@ -538,8 +545,15 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { private returns (uint256) { - if (converter.version() >= 16) { - return converter.convert2(_fromToken, _toToken, _amount, 1, address(0), 0); + (bool ok, bytes memory data) = address(converter).call.gas(300000)( + abi.encodeWithSelector( + converter.convert2.selector, + _fromToken, _toToken, _amount, 1, address(0), 0 + ) + ); + + if (ok) { + return abi.decode(data, (uint256)); } return converter.convert(_fromToken, _toToken, _amount, 1); From 1bf38bd16b504367d3235aacc8225ac28074fc8b Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 3 Apr 2020 16:43:31 +0300 Subject: [PATCH 13/60] fix leftover calculation --- contracts/OneSplitSmartToken.sol | 55 +++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 5d6ccaf..1a8d94a 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -243,6 +243,8 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(toToken))); + uint256 tokenAmount; + uint256[] memory dist; uint256[] memory fundAmounts = new uint256[](smartTokenDetails.reserveTokenList.length); for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { @@ -252,16 +254,14 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { smartTokenDetails.totalReserveTokensRatio ); - uint256 tokenAmount; - uint256[] memory dist; if (smartTokenDetails.reserveTokenList[i].token != fromToken) { - (tokenAmount, dist) = super.getExpectedReturn( + (tokenAmount, dist) = _findBestSwapPrice( fromToken, smartTokenDetails.reserveTokenList[i].token, exchangeAmount, parts, - disableFlags | FLAG_DISABLE_BANCOR + disableFlags ); for (uint j = 0; j < distribution.length; j++) { @@ -284,20 +284,23 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { } + uint256 _minFundAmount = minFundAmount; + // Swap leftovers for SmartToken for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { uint256 reserveBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter); + uint256 totalSupply = toToken.totalSupply(); - uint256 leftover = fundAmounts[i].sub(minFundAmount) + uint256 leftover = fundAmounts[i].sub(_minFundAmount) .mul(reserveBalance) - .div(toToken.totalSupply()); + .div(totalSupply); if (leftover > 0) { minFundAmount = minFundAmount.add( smartTokenFormula.calculatePurchaseReturn( - toToken.totalSupply(), + totalSupply, reserveBalance, uint32(smartTokenDetails.totalReserveTokensRatio), leftover @@ -311,6 +314,42 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { return (minFundAmount, distribution); } + function _findBestSwapPrice( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + private + view + returns (uint256, uint256[] memory) + { + + (uint256 tokenAmountWithBancor, uint256[] memory distWithBancor) = super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + disableFlags + ); + + (uint256 tokenAmount, uint256[] memory dist) = super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + disableFlags | FLAG_DISABLE_BANCOR + ); + + if (tokenAmountWithBancor > tokenAmount) { + return (tokenAmountWithBancor, distWithBancor); + } + + return (tokenAmount, dist); + + } + } @@ -485,7 +524,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { smartTokenDetails.reserveTokenList[i].token, exchangeAmount, dist, - disableFlags | FLAG_DISABLE_BANCOR + disableFlags ); uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); From 9b4919548b6ab2566606390b9efcd4f9352a68d7 Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 3 Apr 2020 23:38:52 +0300 Subject: [PATCH 14/60] safe fund swap --- contracts/IOneSplit.sol | 19 ++++++++++ contracts/OneSplitBase.sol | 1 + contracts/OneSplitSmartToken.sol | 65 +++++++++++++++++++------------- 3 files changed, 58 insertions(+), 27 deletions(-) diff --git a/contracts/IOneSplit.sol b/contracts/IOneSplit.sol index f699af2..d333edf 100644 --- a/contracts/IOneSplit.sol +++ b/contracts/IOneSplit.sol @@ -29,6 +29,25 @@ contract IOneSplitView { uint256 public constant FLAG_DISABLE_CURVE_SYNTHETIX = 0x40000; uint256 public constant FLAG_DISABLE_WETH = 0x80000; + uint256 public constant FLAG_DISABLE_ALL_DEXES = + FLAG_DISABLE_UNISWAP | + FLAG_DISABLE_KYBER | + FLAG_DISABLE_BANCOR | + FLAG_DISABLE_OASIS | + FLAG_DISABLE_COMPOUND | + FLAG_DISABLE_FULCRUM | + FLAG_DISABLE_CHAI | + FLAG_DISABLE_AAVE | + FLAG_DISABLE_SMART_TOKEN | + FLAG_DISABLE_BDAI | + FLAG_DISABLE_IEARN | + FLAG_DISABLE_CURVE_COMPOUND | + FLAG_DISABLE_CURVE_USDT | + FLAG_DISABLE_CURVE_Y | + FLAG_DISABLE_CURVE_BINANCE | + FLAG_DISABLE_CURVE_SYNTHETIX | + FLAG_DISABLE_WETH; + function getExpectedReturn( IERC20 fromToken, IERC20 toToken, diff --git a/contracts/OneSplitBase.sol b/contracts/OneSplitBase.sol index 035cbf9..ea8fae0 100644 --- a/contracts/OneSplitBase.sol +++ b/contracts/OneSplitBase.sol @@ -41,6 +41,7 @@ contract OneSplitBaseBase { IERC20 public tusd = IERC20(0x0000000000085d4780B73119b644AE5ecd22b376); IERC20 public busd = IERC20(0x4Fabb145d64652a948d72533023f6E7A623C7C53); IERC20 public susd = IERC20(0x57Ab1ec28D129707052df4dF418D58a2D46d5f51); + IERC20 public originalSUSD = IERC20(0x57Ab1E02fEE23774580C119740129eAC7081e9D3); IWETH public wethToken = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); IBancorEtherToken public bancorEtherToken = IBancorEtherToken(0xc0829421C1d260BD3cB3E0F06cfE2D52db2cE315); diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 1a8d94a..5d3e452 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -355,6 +355,11 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { + struct Amounts { + uint256[] fund; + uint256[] reserveTokenAmounts; + } + function _swap( IERC20 fromToken, IERC20 toToken, @@ -450,38 +455,37 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(fromToken))); uint256[] memory tokenBalanceBefore = new uint256[](smartTokenDetails.reserveTokenList.length); - uint256[][] memory dist = new uint256[][](smartTokenDetails.reserveTokenList.length); for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { if (smartTokenDetails.reserveTokenList[i].token == toToken) { continue; } - dist[i] = new uint256[](distribution.length); - tokenBalanceBefore[i] = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); - for (uint j = 0; j < distribution.length; j++) { - dist[i][j] = (distribution[j] >> (i * 8)) & 0xFF; - } - } ISmartTokenConverter(smartTokenDetails.converter).liquidate(amount); + uint256[] memory dist = new uint256[](distribution.length); for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { if (smartTokenDetails.reserveTokenList[i].token == toToken) { continue; } + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); super._swap( - smartTokenDetails.reserveTokenList[i].token, + smartTokenDetails.reserveTokenList[i].token == originalSUSD + ? susd : smartTokenDetails.reserveTokenList[i].token, toToken, tokenBalanceAfter.sub(tokenBalanceBefore[i]), - dist[i], + dist, disableFlags ); @@ -497,12 +501,12 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { uint256 disableFlags ) private { + uint256[] memory dist = new uint256[](distribution.length); uint256 minFundAmount = uint256(-1); SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(toToken))); - uint256[] memory dist = new uint256[](distribution.length); - uint256[] memory fundAmounts = new uint256[](smartTokenDetails.reserveTokenList.length); + uint256 curFundAmount; for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { uint256 exchangeAmount = _calcExchangeAmount( @@ -521,7 +525,8 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { super._swap( fromToken, - smartTokenDetails.reserveTokenList[i].token, + smartTokenDetails.reserveTokenList[i].token == originalSUSD + ? susd : smartTokenDetails.reserveTokenList[i].token, exchangeAmount, dist, disableFlags @@ -529,44 +534,50 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); - fundAmounts[i] = toToken.totalSupply() + curFundAmount = toToken.totalSupply() .mul(tokenBalanceAfter.sub(tokenBalanceBefore)) .div(smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter)); } else { - fundAmounts[i] = toToken.totalSupply() + curFundAmount = toToken.totalSupply() .mul(exchangeAmount) .div(smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter)); } - if (fundAmounts[i] < minFundAmount) { - minFundAmount = fundAmounts[i]; + if (curFundAmount < minFundAmount) { + minFundAmount = curFundAmount; } _infiniteApproveIfNeeded(smartTokenDetails.reserveTokenList[i].token, smartTokenDetails.converter); } - ISmartTokenConverter(smartTokenDetails.converter).fund(minFundAmount); + smartTokenDetails.converter.call.gas(600000)( + abi.encodeWithSelector( + ISmartTokenConverter(smartTokenDetails.converter).fund.selector, + minFundAmount + ) + ); + + dist = new uint256[](distribution.length); + dist[2] = 1; // Swap leftovers for SmartToken for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { - uint256 reserveBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter); - - uint256 leftover = fundAmounts[i].sub(minFundAmount) - .mul(reserveBalance) - .div(toToken.totalSupply()); + uint256 tokenBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); - if (leftover > 0) { + if (tokenBalance > 0) { - convert( - ISmartTokenConverter(smartTokenDetails.converter), - smartTokenDetails.reserveTokenList[i].token, + super._swap( + smartTokenDetails.reserveTokenList[i].token == originalSUSD + ? susd : smartTokenDetails.reserveTokenList[i].token, toToken, - leftover + tokenBalance, + dist, + FLAG_DISABLE_ALL_DEXES - FLAG_DISABLE_BANCOR ); } From e79ff4226411ed1ae57ade7aedb65a574c869dc2 Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 4 Apr 2020 00:47:15 +0300 Subject: [PATCH 15/60] refactor --- contracts/OneSplitSmartToken.sol | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 5d3e452..2dc18ec 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -554,7 +554,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { } - smartTokenDetails.converter.call.gas(600000)( + smartTokenDetails.converter.call.gas(1000000)( abi.encodeWithSelector( ISmartTokenConverter(smartTokenDetails.converter).fund.selector, minFundAmount @@ -569,18 +569,22 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { uint256 tokenBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); - if (tokenBalance > 0) { + if (tokenBalance == 0) { + continue; + } - super._swap( + address(this).staticcall.gas(1000000)( + abi.encodeWithSelector( + this.swap.selector, smartTokenDetails.reserveTokenList[i].token == originalSUSD - ? susd : smartTokenDetails.reserveTokenList[i].token, + ? susd : smartTokenDetails.reserveTokenList[i].token, toToken, tokenBalance, + 0, dist, FLAG_DISABLE_ALL_DEXES - FLAG_DISABLE_BANCOR - ); - - } + ) + ); } From d1134877a1e7292b19f43f57ff18f2c2b92f2b2e Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 4 Apr 2020 14:37:18 +0300 Subject: [PATCH 16/60] handle "lose funds" behavior --- contracts/OneSplitSmartToken.sol | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 2dc18ec..f2118f3 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -573,11 +573,11 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { continue; } - address(this).staticcall.gas(1000000)( + (bool successExchange, ) = address(this).call.gas(1000000)( abi.encodeWithSelector( this.swap.selector, smartTokenDetails.reserveTokenList[i].token == originalSUSD - ? susd : smartTokenDetails.reserveTokenList[i].token, + ? susd : smartTokenDetails.reserveTokenList[i].token, toToken, tokenBalance, 0, @@ -586,6 +586,12 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { ) ); + if (successExchange) { + continue; + } + + smartTokenDetails.reserveTokenList[i].token.transfer(msg.sender, tokenBalance); + } } From f5a24bd209febd760dbb28e78c938a9cb40a5542 Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 4 Apr 2020 17:24:27 +0300 Subject: [PATCH 17/60] some changes and safe calls --- contracts/IOneSplit.sol | 2 +- contracts/OneSplitSmartToken.sol | 124 +++++++++++++++---------------- 2 files changed, 62 insertions(+), 64 deletions(-) diff --git a/contracts/IOneSplit.sol b/contracts/IOneSplit.sol index d333edf..8bd975f 100644 --- a/contracts/IOneSplit.sol +++ b/contracts/IOneSplit.sol @@ -29,7 +29,7 @@ contract IOneSplitView { uint256 public constant FLAG_DISABLE_CURVE_SYNTHETIX = 0x40000; uint256 public constant FLAG_DISABLE_WETH = 0x80000; - uint256 public constant FLAG_DISABLE_ALL_DEXES = + uint256 public constant FLAG_DISABLE_ALL = FLAG_DISABLE_UNISWAP | FLAG_DISABLE_KYBER | FLAG_DISABLE_BANCOR | diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index f2118f3..d0df02c 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -209,7 +209,8 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { } (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( - smartTokenDetails.reserveTokenList[i].token, + smartTokenDetails.reserveTokenList[i].token == originalSUSD + ? susd : smartTokenDetails.reserveTokenList[i].token, toToken, srcAmount, parts, @@ -256,9 +257,10 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { if (smartTokenDetails.reserveTokenList[i].token != fromToken) { - (tokenAmount, dist) = _findBestSwapPrice( + (tokenAmount, dist) = super.getExpectedReturn( fromToken, - smartTokenDetails.reserveTokenList[i].token, + smartTokenDetails.reserveTokenList[i].token == originalSUSD + ? susd : smartTokenDetails.reserveTokenList[i].token, exchangeAmount, parts, disableFlags @@ -285,36 +287,39 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { } uint256 _minFundAmount = minFundAmount; + IERC20 _toToken = toToken; // Swap leftovers for SmartToken for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + if (_minFundAmount == fundAmounts[i]) { + continue; + } + uint256 reserveBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter); - uint256 totalSupply = toToken.totalSupply(); + uint256 totalSmartTokenSupply = _toToken.totalSupply(); uint256 leftover = fundAmounts[i].sub(_minFundAmount) .mul(reserveBalance) - .div(totalSupply); + .div(totalSmartTokenSupply); - if (leftover > 0) { + (tokenAmount, dist) = super.getExpectedReturn( + smartTokenDetails.reserveTokenList[i].token == originalSUSD + ? susd : smartTokenDetails.reserveTokenList[i].token, + _toToken, + leftover, + 1, + FLAG_DISABLE_ALL - FLAG_DISABLE_BANCOR + ); - minFundAmount = minFundAmount.add( - smartTokenFormula.calculatePurchaseReturn( - totalSupply, - reserveBalance, - uint32(smartTokenDetails.totalReserveTokensRatio), - leftover - ) - ); - - } + minFundAmount = minFundAmount.add(tokenAmount); } return (minFundAmount, distribution); } - function _findBestSwapPrice( + function _safeGetExpectedReturn( IERC20 fromToken, IERC20 toToken, uint256 amount, @@ -323,31 +328,24 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { ) private view - returns (uint256, uint256[] memory) + returns (uint256 returnAmount, uint256[] memory distribution) { - - (uint256 tokenAmountWithBancor, uint256[] memory distWithBancor) = super.getExpectedReturn( - fromToken, - toToken, - amount, - parts, - disableFlags - ); - - (uint256 tokenAmount, uint256[] memory dist) = super.getExpectedReturn( - fromToken, - toToken, - amount, - parts, - disableFlags | FLAG_DISABLE_BANCOR + (bool successExchange, bytes memory data) = address(this).staticcall.gas(600000)( + abi.encodeWithSelector( + this.getExpectedReturn.selector, + fromToken, + toToken, + amount, + parts, + disableFlags + ) ); - if (tokenAmountWithBancor > tokenAmount) { - return (tokenAmountWithBancor, distWithBancor); + if (!successExchange) { + return (returnAmount, new uint256[](9)); } - return (tokenAmount, dist); - + return abi.decode(data, (uint256, uint256[])); } } @@ -554,7 +552,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { } - smartTokenDetails.converter.call.gas(1000000)( + smartTokenDetails.converter.call.gas(2000000)( abi.encodeWithSelector( ISmartTokenConverter(smartTokenDetails.converter).fund.selector, minFundAmount @@ -573,16 +571,16 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { continue; } - (bool successExchange, ) = address(this).call.gas(1000000)( + (bool successExchange, ) = address(this).call.gas(2000000)( abi.encodeWithSelector( this.swap.selector, smartTokenDetails.reserveTokenList[i].token == originalSUSD ? susd : smartTokenDetails.reserveTokenList[i].token, toToken, tokenBalance, - 0, + 1, dist, - FLAG_DISABLE_ALL_DEXES - FLAG_DISABLE_BANCOR + 0 ) ); @@ -596,27 +594,27 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { } - function convert( - ISmartTokenConverter converter, - IERC20 _fromToken, - IERC20 _toToken, - uint256 _amount - ) - private - returns (uint256) - { - (bool ok, bytes memory data) = address(converter).call.gas(300000)( - abi.encodeWithSelector( - converter.convert2.selector, - _fromToken, _toToken, _amount, 1, address(0), 0 - ) - ); - - if (ok) { - return abi.decode(data, (uint256)); - } - - return converter.convert(_fromToken, _toToken, _amount, 1); - } +// function convert( +// ISmartTokenConverter converter, +// IERC20 _fromToken, +// IERC20 _toToken, +// uint256 _amount +// ) +// private +// returns (uint256) +// { +// (bool ok, bytes memory data) = address(converter).call.gas(300000)( +// abi.encodeWithSelector( +// converter.convert2.selector, +// _fromToken, _toToken, _amount, 1, address(0), 0 +// ) +// ); +// +// if (ok) { +// return abi.decode(data, (uint256)); +// } +// +// return converter.convert(_fromToken, _toToken, _amount, 1); +// } } From 1cab6d38e5bbfbcec8cdf7fcbcaf163615e6bf22 Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 4 Apr 2020 20:03:23 +0300 Subject: [PATCH 18/60] disable flags -> 0 --- contracts/OneSplitSmartToken.sol | 136 ++++++++++++++++++------------- 1 file changed, 80 insertions(+), 56 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index d0df02c..b854fe1 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -119,7 +119,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { bntToken, amount, parts, - disableFlags + 0 ); ( @@ -130,7 +130,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { toToken, returnBntAmount, parts, - disableFlags + 0 ); for (uint i = 0; i < smartTokenToDistribution.length; i++) { @@ -149,7 +149,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { toToken, amount, parts, - disableFlags + 0 ); } @@ -161,7 +161,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { toToken, amount, parts, - disableFlags + 0 ); } @@ -260,7 +260,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { (tokenAmount, dist) = super.getExpectedReturn( fromToken, smartTokenDetails.reserveTokenList[i].token == originalSUSD - ? susd : smartTokenDetails.reserveTokenList[i].token, + ? susd : smartTokenDetails.reserveTokenList[i].token, exchangeAmount, parts, disableFlags @@ -389,7 +389,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { bntToken, amount, dist, - disableFlags + 0 ); for (uint i = 0; i < distribution.length; i++) { @@ -403,7 +403,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { toToken, bntBalanceAfter.sub(bntBalanceBefore), dist, - disableFlags + 0 ); } @@ -415,7 +415,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { toToken, amount, distribution, - disableFlags + 0 ); } @@ -427,7 +427,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { toToken, amount, distribution, - disableFlags + 0 ); } @@ -452,18 +452,12 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(fromToken))); - uint256[] memory tokenBalanceBefore = new uint256[](smartTokenDetails.reserveTokenList.length); - for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { - - if (smartTokenDetails.reserveTokenList[i].token == toToken) { - continue; - } - - tokenBalanceBefore[i] = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); - - } - - ISmartTokenConverter(smartTokenDetails.converter).liquidate(amount); + (bool ok, ) = smartTokenDetails.converter.call.gas(2000000)( + abi.encodeWithSelector( + ISmartTokenConverter(smartTokenDetails.converter).liquidate.selector, + amount + ) + ); uint256[] memory dist = new uint256[](distribution.length); for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { @@ -476,15 +470,33 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { dist[j] = (distribution[j] >> (i * 8)) & 0xFF; } - uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); + uint256 tokenBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); + if (tokenBalance == 0) { + continue; + } + + if (ok) { + + _safeTokenSwap( + smartTokenDetails.reserveTokenList[i].token == originalSUSD + ? susd : smartTokenDetails.reserveTokenList[i].token, + toToken, + tokenBalance, + 0, + dist, + disableFlags + ); + + continue; + } super._swap( smartTokenDetails.reserveTokenList[i].token == originalSUSD ? susd : smartTokenDetails.reserveTokenList[i].token, toToken, - tokenBalanceAfter.sub(tokenBalanceBefore[i]), + tokenBalance, dist, - disableFlags + FLAG_DISABLE_SMART_TOKEN ); } @@ -552,7 +564,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { } - smartTokenDetails.converter.call.gas(2000000)( + (bool ok, ) = smartTokenDetails.converter.call.gas(2000000)( abi.encodeWithSelector( ISmartTokenConverter(smartTokenDetails.converter).fund.selector, minFundAmount @@ -571,50 +583,62 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { continue; } - (bool successExchange, ) = address(this).call.gas(2000000)( - abi.encodeWithSelector( - this.swap.selector, + if (ok) { + log0(bytes32(tokenBalance)); + + _safeTokenSwap( smartTokenDetails.reserveTokenList[i].token == originalSUSD ? susd : smartTokenDetails.reserveTokenList[i].token, toToken, tokenBalance, - 1, + 0, dist, - 0 - ) - ); + FLAG_DISABLE_SMART_TOKEN + ); - if (successExchange) { continue; + } - smartTokenDetails.reserveTokenList[i].token.transfer(msg.sender, tokenBalance); + super._swap( + smartTokenDetails.reserveTokenList[i].token == originalSUSD + ? susd : smartTokenDetails.reserveTokenList[i].token, + toToken, + tokenBalance, + dist, + FLAG_DISABLE_SMART_TOKEN + ); } } -// function convert( -// ISmartTokenConverter converter, -// IERC20 _fromToken, -// IERC20 _toToken, -// uint256 _amount -// ) -// private -// returns (uint256) -// { -// (bool ok, bytes memory data) = address(converter).call.gas(300000)( -// abi.encodeWithSelector( -// converter.convert2.selector, -// _fromToken, _toToken, _amount, 1, address(0), 0 -// ) -// ); -// -// if (ok) { -// return abi.decode(data, (uint256)); -// } -// -// return converter.convert(_fromToken, _toToken, _amount, 1); -// } + function _safeTokenSwap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 minReturnAmount, + uint256[] memory distribution, + uint256 disableFlags + ) private { + + (bool successExchange, ) = address(this).call.gas(2000000)( + abi.encodeWithSelector( + this.swap.selector, + fromToken, + toToken, + amount, + minReturnAmount, + distribution, + disableFlags + ) + ); + + if (successExchange) { + return; + } + + fromToken.transfer(msg.sender, amount); + } } From e3995a4ad1b44e871414605c08d8747c5391a6f0 Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 4 Apr 2020 20:26:33 +0300 Subject: [PATCH 19/60] remove useless code --- contracts/OneSplitSmartToken.sol | 5 ----- 1 file changed, 5 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index b854fe1..d0d16cf 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -353,11 +353,6 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { - struct Amounts { - uint256[] fund; - uint256[] reserveTokenAmounts; - } - function _swap( IERC20 fromToken, IERC20 toToken, From bf2d661fe1aaaac8e4a2fecb82a4ecf74b8717ce Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 4 Apr 2020 23:14:36 +0300 Subject: [PATCH 20/60] refactor --- contracts/IOneSplit.sol | 16 +++-- contracts/OneSplitSmartToken.sol | 119 ++++++++++--------------------- 2 files changed, 47 insertions(+), 88 deletions(-) diff --git a/contracts/IOneSplit.sol b/contracts/IOneSplit.sol index 8bd975f..40027c5 100644 --- a/contracts/IOneSplit.sol +++ b/contracts/IOneSplit.sol @@ -29,11 +29,18 @@ contract IOneSplitView { uint256 public constant FLAG_DISABLE_CURVE_SYNTHETIX = 0x40000; uint256 public constant FLAG_DISABLE_WETH = 0x80000; - uint256 public constant FLAG_DISABLE_ALL = + uint256 public constant FLAG_DISABLE_ALL_SOURCES = FLAG_DISABLE_UNISWAP | FLAG_DISABLE_KYBER | FLAG_DISABLE_BANCOR | FLAG_DISABLE_OASIS | + FLAG_DISABLE_CURVE_COMPOUND | + FLAG_DISABLE_CURVE_USDT | + FLAG_DISABLE_CURVE_Y | + FLAG_DISABLE_CURVE_BINANCE | + FLAG_DISABLE_CURVE_SYNTHETIX; + + uint256 public constant FLAG_DISABLE_ALL_WRAPPERS = FLAG_DISABLE_COMPOUND | FLAG_DISABLE_FULCRUM | FLAG_DISABLE_CHAI | @@ -41,13 +48,10 @@ contract IOneSplitView { FLAG_DISABLE_SMART_TOKEN | FLAG_DISABLE_BDAI | FLAG_DISABLE_IEARN | - FLAG_DISABLE_CURVE_COMPOUND | - FLAG_DISABLE_CURVE_USDT | - FLAG_DISABLE_CURVE_Y | - FLAG_DISABLE_CURVE_BINANCE | - FLAG_DISABLE_CURVE_SYNTHETIX | FLAG_DISABLE_WETH; + uint256 public constant FLAG_DISABLE_ALL = FLAG_DISABLE_ALL_SOURCES | FLAG_DISABLE_ALL_WRAPPERS; + function getExpectedReturn( IERC20 fromToken, IERC20 toToken, diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index d0d16cf..f6adc45 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -12,6 +12,7 @@ contract OneSplitSmartTokenBase { ISmartTokenRegistry smartTokenRegistry = ISmartTokenRegistry(0xf6E2D7F616B67E46D708e4410746E9AAb3a4C518); ISmartTokenFormula smartTokenFormula = ISmartTokenFormula(0x524619EB9b4cdFFa7DA13029b33f24635478AFc0); IERC20 bntToken = IERC20(0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C); + IERC20 usdbToken = IERC20(0x309627af60F0926daa6041B8279484312f2bf060); struct TokenWithRatio { IERC20 token; @@ -22,15 +23,21 @@ contract OneSplitSmartTokenBase { TokenWithRatio[] reserveTokenList; address converter; uint256 totalReserveTokensRatio; + bool hasHubToken; } function _getSmartTokenDetails(ISmartToken smartToken) internal view returns (SmartTokenDetails memory details) { ISmartTokenConverter converter = smartToken.owner(); - (TokenWithRatio[] memory reserveTokenList, uint256 totalReserveTokensRatio) = _getTokens(converter); + ( + TokenWithRatio[] memory reserveTokenList, + uint256 totalReserveTokensRatio, + bool hasHubToken + ) = _getTokens(converter); details.reserveTokenList = reserveTokenList; details.converter = address(converter); details.totalReserveTokensRatio = totalReserveTokensRatio; + details.hasHubToken = hasHubToken; return details; } @@ -40,15 +47,23 @@ contract OneSplitSmartTokenBase { ) internal view - returns(TokenWithRatio[] memory reserveTokenList, uint256 totalRatio) + returns(TokenWithRatio[] memory reserveTokenList, uint256 totalRatio, bool hasHubToken) { reserveTokenList = new TokenWithRatio[](converter.connectorTokenCount()); for (uint256 i = 0; i < reserveTokenList.length; i++) { reserveTokenList[i].token = converter.connectorTokens(i); reserveTokenList[i].ratio = _getReserveRatio(converter, reserveTokenList[i].token); totalRatio = totalRatio.add(reserveTokenList[i].ratio); + + hasHubToken = hasHubToken || + ( + reserveTokenList[i].token == bntToken || + reserveTokenList[i].token == usdbToken + ); + } - return (reserveTokenList, totalRatio); + + return (reserveTokenList, totalRatio, hasHubToken); } function _getReserveRatio( @@ -208,7 +223,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { continue; } - (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( + (uint256 ret, uint256[] memory dist) = getExpectedReturn( smartTokenDetails.reserveTokenList[i].token == originalSUSD ? susd : smartTokenDetails.reserveTokenList[i].token, toToken, @@ -257,7 +272,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { if (smartTokenDetails.reserveTokenList[i].token != fromToken) { - (tokenAmount, dist) = super.getExpectedReturn( + (tokenAmount, dist) = getExpectedReturn( fromToken, smartTokenDetails.reserveTokenList[i].token == originalSUSD ? susd : smartTokenDetails.reserveTokenList[i].token, @@ -286,6 +301,10 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { } + if (!smartTokenDetails.hasHubToken) { + return (minFundAmount, distribution); + } + uint256 _minFundAmount = minFundAmount; IERC20 _toToken = toToken; @@ -303,13 +322,13 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { .mul(reserveBalance) .div(totalSmartTokenSupply); - (tokenAmount, dist) = super.getExpectedReturn( + (tokenAmount, dist) = getExpectedReturn( smartTokenDetails.reserveTokenList[i].token == originalSUSD ? susd : smartTokenDetails.reserveTokenList[i].token, _toToken, leftover, 1, - FLAG_DISABLE_ALL - FLAG_DISABLE_BANCOR + FLAG_DISABLE_ALL ^ FLAG_DISABLE_BANCOR ); minFundAmount = minFundAmount.add(tokenAmount); @@ -319,35 +338,6 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { return (minFundAmount, distribution); } - function _safeGetExpectedReturn( - IERC20 fromToken, - IERC20 toToken, - uint256 amount, - uint256 parts, - uint256 disableFlags - ) - private - view - returns (uint256 returnAmount, uint256[] memory distribution) - { - (bool successExchange, bytes memory data) = address(this).staticcall.gas(600000)( - abi.encodeWithSelector( - this.getExpectedReturn.selector, - fromToken, - toToken, - amount, - parts, - disableFlags - ) - ); - - if (!successExchange) { - return (returnAmount, new uint256[](9)); - } - - return abi.decode(data, (uint256, uint256[])); - } - } @@ -447,12 +437,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(fromToken))); - (bool ok, ) = smartTokenDetails.converter.call.gas(2000000)( - abi.encodeWithSelector( - ISmartTokenConverter(smartTokenDetails.converter).liquidate.selector, - amount - ) - ); + ISmartTokenConverter(smartTokenDetails.converter).liquidate(amount); uint256[] memory dist = new uint256[](distribution.length); for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { @@ -470,28 +455,14 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { continue; } - if (ok) { - - _safeTokenSwap( - smartTokenDetails.reserveTokenList[i].token == originalSUSD - ? susd : smartTokenDetails.reserveTokenList[i].token, - toToken, - tokenBalance, - 0, - dist, - disableFlags - ); - - continue; - } - - super._swap( + _safeTokenSwap( smartTokenDetails.reserveTokenList[i].token == originalSUSD ? susd : smartTokenDetails.reserveTokenList[i].token, toToken, tokenBalance, + 0, dist, - FLAG_DISABLE_SMART_TOKEN + disableFlags ); } @@ -528,7 +499,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { dist[j] = (distribution[j] >> (i * 8)) & 0xFF; } - super._swap( + _swap( fromToken, smartTokenDetails.reserveTokenList[i].token == originalSUSD ? susd : smartTokenDetails.reserveTokenList[i].token, @@ -559,12 +530,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { } - (bool ok, ) = smartTokenDetails.converter.call.gas(2000000)( - abi.encodeWithSelector( - ISmartTokenConverter(smartTokenDetails.converter).fund.selector, - minFundAmount - ) - ); + ISmartTokenConverter(smartTokenDetails.converter).fund(minFundAmount); dist = new uint256[](distribution.length); dist[2] = 1; @@ -578,28 +544,17 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { continue; } - if (ok) { - log0(bytes32(tokenBalance)); - - _safeTokenSwap( - smartTokenDetails.reserveTokenList[i].token == originalSUSD - ? susd : smartTokenDetails.reserveTokenList[i].token, - toToken, - tokenBalance, - 0, - dist, - FLAG_DISABLE_SMART_TOKEN - ); - + if (!smartTokenDetails.hasHubToken) { + smartTokenDetails.reserveTokenList[i].token.transfer(msg.sender, tokenBalance); continue; - } - super._swap( + _safeTokenSwap( smartTokenDetails.reserveTokenList[i].token == originalSUSD ? susd : smartTokenDetails.reserveTokenList[i].token, toToken, tokenBalance, + 0, dist, FLAG_DISABLE_SMART_TOKEN ); @@ -617,7 +572,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { uint256 disableFlags ) private { - (bool successExchange, ) = address(this).call.gas(2000000)( + (bool successExchange, ) = address(this).call.gas(600000)( abi.encodeWithSelector( this.swap.selector, fromToken, From a975fb6f7da801ebb1faf2750ce742abf5222a9d Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Sun, 5 Apr 2020 00:57:17 +0300 Subject: [PATCH 21/60] Some fixes --- contracts/OneSplit.sol | 11 +- contracts/OneSplitBase.sol | 1 - contracts/OneSplitSmartToken.sol | 289 ++++++++++++------------------- test/OneSplit.js | 8 +- 4 files changed, 118 insertions(+), 191 deletions(-) diff --git a/contracts/OneSplit.sol b/contracts/OneSplit.sol index 24f48ac..3678351 100644 --- a/contracts/OneSplit.sol +++ b/contracts/OneSplit.sol @@ -105,14 +105,19 @@ contract OneSplit is uint256[] memory distribution, // [Uniswap, Kyber, Bancor, Oasis] uint256 disableFlags // 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI ) public payable { - fromToken.universalTransferFrom(msg.sender, address(this), amount); + if (msg.sender != address(this)) { + fromToken.universalTransferFrom(msg.sender, address(this), amount); + } _swap(fromToken, toToken, amount, distribution, disableFlags); uint256 returnAmount = toToken.universalBalanceOf(address(this)); require(returnAmount >= minReturn, "OneSplit: actual return amount is less than minReturn"); - toToken.universalTransfer(msg.sender, returnAmount); - fromToken.universalTransfer(msg.sender, fromToken.universalBalanceOf(address(this))); + + if (msg.sender != address(this)) { + toToken.universalTransfer(msg.sender, returnAmount); + fromToken.universalTransfer(msg.sender, fromToken.universalBalanceOf(address(this))); + } } function _swap( diff --git a/contracts/OneSplitBase.sol b/contracts/OneSplitBase.sol index ea8fae0..035cbf9 100644 --- a/contracts/OneSplitBase.sol +++ b/contracts/OneSplitBase.sol @@ -41,7 +41,6 @@ contract OneSplitBaseBase { IERC20 public tusd = IERC20(0x0000000000085d4780B73119b644AE5ecd22b376); IERC20 public busd = IERC20(0x4Fabb145d64652a948d72533023f6E7A623C7C53); IERC20 public susd = IERC20(0x57Ab1ec28D129707052df4dF418D58a2D46d5f51); - IERC20 public originalSUSD = IERC20(0x57Ab1E02fEE23774580C119740129eAC7081e9D3); IWETH public wethToken = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); IBancorEtherToken public bancorEtherToken = IBancorEtherToken(0xc0829421C1d260BD3cB3E0F06cfE2D52db2cE315); diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index f6adc45..9afb7bd 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -6,6 +6,7 @@ import "./interface/ISmartTokenConverter.sol"; import "./interface/ISmartTokenFormula.sol"; import "./OneSplitBase.sol"; + contract OneSplitSmartTokenBase { using SafeMath for uint256; @@ -14,56 +15,34 @@ contract OneSplitSmartTokenBase { IERC20 bntToken = IERC20(0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C); IERC20 usdbToken = IERC20(0x309627af60F0926daa6041B8279484312f2bf060); + IERC20 public susd = IERC20(0x57Ab1ec28D129707052df4dF418D58a2D46d5f51); + IERC20 public acientSUSD = IERC20(0x57Ab1E02fEE23774580C119740129eAC7081e9D3); + struct TokenWithRatio { IERC20 token; uint256 ratio; } struct SmartTokenDetails { - TokenWithRatio[] reserveTokenList; + TokenWithRatio[] tokens; address converter; - uint256 totalReserveTokensRatio; - bool hasHubToken; + uint256 totalRatio; } - function _getSmartTokenDetails(ISmartToken smartToken) internal view returns (SmartTokenDetails memory details) { - ISmartTokenConverter converter = smartToken.owner(); - ( - TokenWithRatio[] memory reserveTokenList, - uint256 totalReserveTokensRatio, - bool hasHubToken - ) = _getTokens(converter); - - details.reserveTokenList = reserveTokenList; - details.converter = address(converter); - details.totalReserveTokensRatio = totalReserveTokensRatio; - details.hasHubToken = hasHubToken; - - return details; - } - - function _getTokens( - ISmartTokenConverter converter - ) + function _getSmartTokenDetails(ISmartToken smartToken) internal view - returns(TokenWithRatio[] memory reserveTokenList, uint256 totalRatio, bool hasHubToken) + returns(SmartTokenDetails memory details) { - reserveTokenList = new TokenWithRatio[](converter.connectorTokenCount()); - for (uint256 i = 0; i < reserveTokenList.length; i++) { - reserveTokenList[i].token = converter.connectorTokens(i); - reserveTokenList[i].ratio = _getReserveRatio(converter, reserveTokenList[i].token); - totalRatio = totalRatio.add(reserveTokenList[i].ratio); - - hasHubToken = hasHubToken || - ( - reserveTokenList[i].token == bntToken || - reserveTokenList[i].token == usdbToken - ); + ISmartTokenConverter converter = smartToken.owner(); + details.converter = address(converter); + details.tokens = new TokenWithRatio[](converter.connectorTokenCount()); + for (uint256 i = 0; i < details.tokens.length; i++) { + details.tokens[i].token = converter.connectorTokens(i); + details.tokens[i].ratio = _getReserveRatio(converter, details.tokens[i].token); + details.totalRatio = details.totalRatio.add(details.tokens[i].ratio); } - - return (reserveTokenList, totalRatio, hasHubToken); } function _getReserveRatio( @@ -74,15 +53,14 @@ contract OneSplitSmartTokenBase { view returns (uint256) { - - (bool ok, bytes memory data) = address(converter).staticcall.gas(10000)( + (bool success, bytes memory data) = address(converter).staticcall.gas(10000)( abi.encodeWithSelector( converter.getReserveRatio.selector, token ) ); - if (ok) { + if (success) { return abi.decode(data, (uint256)); } @@ -91,10 +69,9 @@ contract OneSplitSmartTokenBase { return uint256(ratio); } - function _calcExchangeAmount(uint256 amount, uint256 ratio, uint256 totalRatio) internal pure returns (uint256) { - return amount.mul(ratio).div(totalRatio); + function _canonicalSUSD(IERC20 token) internal view returns(IERC20) { + return token == acientSUSD ? susd : token; } - } @@ -114,18 +91,15 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { uint256[] memory ) { - if (fromToken == toToken) { return (amount, new uint256[](9)); } if (!disableFlags.check(FLAG_DISABLE_SMART_TOKEN)) { - bool isSmartTokenFrom = smartTokenRegistry.isSmartToken(fromToken); bool isSmartTokenTo = smartTokenRegistry.isSmartToken(toToken); if (isSmartTokenFrom && isSmartTokenTo) { - ( uint256 returnBntAmount, uint256[] memory smartTokenFromDistribution @@ -149,16 +123,13 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { ); for (uint i = 0; i < smartTokenToDistribution.length; i++) { - smartTokenFromDistribution[i] += smartTokenFromDistribution[i] - .add(smartTokenToDistribution[i] << 128); + smartTokenFromDistribution[i] |= smartTokenToDistribution[i] << 128; } return (returnSmartTokenToAmount, smartTokenFromDistribution); - } if (isSmartTokenFrom) { - return _getExpectedReturnFromSmartToken( fromToken, toToken, @@ -166,11 +137,9 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { parts, 0 ); - } if (isSmartTokenTo) { - return _getExpectedReturnToSmartToken( fromToken, toToken, @@ -178,7 +147,6 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { parts, 0 ); - } } @@ -192,7 +160,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { } function _getExpectedReturnFromSmartToken( - IERC20 fromToken, + IERC20 smartToken, IERC20 toToken, uint256 amount, uint256 parts, @@ -207,25 +175,23 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { { distribution = new uint256[](9); - SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(fromToken))); - - for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); + for (uint i = 0; i < details.tokens.length; i++) { uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( - fromToken.totalSupply(), - smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter), - uint32(smartTokenDetails.totalReserveTokensRatio), + smartToken.totalSupply(), + details.tokens[i].token.balanceOf(details.converter), + uint32(details.totalRatio), amount ); - if (smartTokenDetails.reserveTokenList[i].token == toToken) { + if (details.tokens[i].token == toToken) { returnAmount = returnAmount.add(srcAmount); continue; } (uint256 ret, uint256[] memory dist) = getExpectedReturn( - smartTokenDetails.reserveTokenList[i].token == originalSUSD - ? susd : smartTokenDetails.reserveTokenList[i].token, + _canonicalSUSD(details.tokens[i].token), toToken, srcAmount, parts, @@ -234,15 +200,16 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { returnAmount = returnAmount.add(ret); for (uint j = 0; j < distribution.length; j++) { - distribution[j] = distribution[j].add(dist[j] << (i * 8)); + distribution[j] |= dist[j] << (i * 8); } } + return (returnAmount, distribution); } function _getExpectedReturnToSmartToken( IERC20 fromToken, - IERC20 toToken, + IERC20 smartToken, uint256 amount, uint256 parts, uint256 disableFlags @@ -257,87 +224,75 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { distribution = new uint256[](9); minFundAmount = uint256(-1); - SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(toToken))); + SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); - uint256 tokenAmount; + uint256[] memory tokenAmounts; uint256[] memory dist; - uint256[] memory fundAmounts = new uint256[](smartTokenDetails.reserveTokenList.length); - for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { - - uint256 exchangeAmount = _calcExchangeAmount( - amount, - smartTokenDetails.reserveTokenList[i].ratio, - smartTokenDetails.totalReserveTokensRatio - ); + uint256[] memory fundAmounts = new uint256[](details.tokens.length); - if (smartTokenDetails.reserveTokenList[i].token != fromToken) { + for (uint i = 0; i < details.tokens.length; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].ratio) + .div(details.totalRatio); - (tokenAmount, dist) = getExpectedReturn( + if (details.tokens[i].token != fromToken) { + (tokenAmounts[i], dist) = getExpectedReturn( fromToken, - smartTokenDetails.reserveTokenList[i].token == originalSUSD - ? susd : smartTokenDetails.reserveTokenList[i].token, + _canonicalSUSD(details.tokens[i].token), exchangeAmount, parts, disableFlags ); for (uint j = 0; j < distribution.length; j++) { - distribution[j] = distribution[j].add(dist[j] << (i * 8)); + distribution[j] |= dist[j] << (i * 8); } - } else { - - tokenAmount = exchangeAmount; - + tokenAmounts[i] = exchangeAmount; } - fundAmounts[i] = toToken.totalSupply() - .mul(tokenAmount) - .div(smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter)); + fundAmounts[i] = smartTokenFormula.calculatePurchaseReturn( + smartToken.totalSupply(), + details.tokens[i].token.balanceOf(details.converter), + uint32(details.totalRatio), + tokenAmounts[i] + ); if (fundAmounts[i] < minFundAmount) { minFundAmount = fundAmounts[i]; } - - } - - if (!smartTokenDetails.hasHubToken) { - return (minFundAmount, distribution); } uint256 _minFundAmount = minFundAmount; - IERC20 _toToken = toToken; + IERC20 _smartToken = smartToken; // Swap leftovers for SmartToken - for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { - + for (uint i = 0; i < details.tokens.length; i++) { if (_minFundAmount == fundAmounts[i]) { continue; } - uint256 reserveBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter); - uint256 totalSmartTokenSupply = _toToken.totalSupply(); - - uint256 leftover = fundAmounts[i].sub(_minFundAmount) - .mul(reserveBalance) - .div(totalSmartTokenSupply); + uint256 leftover = tokenAmounts[i].sub( + smartTokenFormula.calculateLiquidateReturn( + _smartToken.totalSupply(), + details.tokens[i].token.balanceOf(details.converter), + uint32(details.totalRatio), + fundAmounts[i] + ) + ); - (tokenAmount, dist) = getExpectedReturn( - smartTokenDetails.reserveTokenList[i].token == originalSUSD - ? susd : smartTokenDetails.reserveTokenList[i].token, - _toToken, + uint256 tokenRet = calculateBancorReturn( + _canonicalSUSD(details.tokens[i].token), + _smartToken, leftover, - 1, - FLAG_DISABLE_ALL ^ FLAG_DISABLE_BANCOR + disableFlags ); - minFundAmount = minFundAmount.add(tokenAmount); - + minFundAmount = minFundAmount.add(tokenRet); } return (minFundAmount, distribution); } - } @@ -361,7 +316,6 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { bool isSmartTokenTo = smartTokenRegistry.isSmartToken(toToken); if (isSmartTokenFrom && isSmartTokenTo) { - uint256[] memory dist = new uint256[](distribution.length); for (uint i = 0; i < distribution.length; i++) { dist[i] = distribution[i] & ((1 << 128) - 1); @@ -390,11 +344,9 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { dist, 0 ); - } if (isSmartTokenFrom) { - return _swapFromSmartToken( fromToken, toToken, @@ -402,11 +354,9 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { distribution, 0 ); - } if (isSmartTokenTo) { - return _swapToSmartToken( fromToken, toToken, @@ -414,7 +364,6 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { distribution, 0 ); - } } @@ -428,21 +377,20 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { } function _swapFromSmartToken( - IERC20 fromToken, + IERC20 smartToken, IERC20 toToken, uint256 amount, uint256[] memory distribution, uint256 disableFlags ) private { + SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); - SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(fromToken))); - - ISmartTokenConverter(smartTokenDetails.converter).liquidate(amount); + ISmartTokenConverter(details.converter).liquidate(amount); uint256[] memory dist = new uint256[](distribution.length); - for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { - if (smartTokenDetails.reserveTokenList[i].token == toToken) { + for (uint i = 0; i < details.tokens.length; i++) { + if (details.tokens[i].token == toToken) { continue; } @@ -450,28 +398,20 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { dist[j] = (distribution[j] >> (i * 8)) & 0xFF; } - uint256 tokenBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); - if (tokenBalance == 0) { - continue; - } - _safeTokenSwap( - smartTokenDetails.reserveTokenList[i].token == originalSUSD - ? susd : smartTokenDetails.reserveTokenList[i].token, + _canonicalSUSD(details.tokens[i].token), toToken, - tokenBalance, + details.tokens[i].token.balanceOf(address(this)), 0, dist, disableFlags ); - } - } function _swapToSmartToken( IERC20 fromToken, - IERC20 toToken, + IERC20 smartToken, uint256 amount, uint256[] memory distribution, uint256 disableFlags @@ -480,87 +420,70 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { uint256[] memory dist = new uint256[](distribution.length); uint256 minFundAmount = uint256(-1); - SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(toToken))); + SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); uint256 curFundAmount; - for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + for (uint i = 0; i < details.tokens.length; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].ratio) + .div(details.totalRatio); - uint256 exchangeAmount = _calcExchangeAmount( - amount, - smartTokenDetails.reserveTokenList[i].ratio, - smartTokenDetails.totalReserveTokensRatio - ); + if (details.tokens[i].token != fromToken) { - if (smartTokenDetails.reserveTokenList[i].token != fromToken) { - - uint256 tokenBalanceBefore = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); + uint256 tokenBalanceBefore = details.tokens[i].token.balanceOf(address(this)); for (uint j = 0; j < distribution.length; j++) { dist[j] = (distribution[j] >> (i * 8)) & 0xFF; } - _swap( + super._swap( fromToken, - smartTokenDetails.reserveTokenList[i].token == originalSUSD - ? susd : smartTokenDetails.reserveTokenList[i].token, + _canonicalSUSD(details.tokens[i].token), exchangeAmount, dist, disableFlags ); - uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); - - curFundAmount = toToken.totalSupply() - .mul(tokenBalanceAfter.sub(tokenBalanceBefore)) - .div(smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter)); + uint256 tokenBalanceAfter = details.tokens[i].token.balanceOf(address(this)); + curFundAmount = smartTokenFormula.calculatePurchaseReturn( + smartToken.totalSupply(), + details.tokens[i].token.balanceOf(details.converter), + uint32(details.totalRatio), + tokenBalanceAfter.sub(tokenBalanceBefore) + ); } else { - - curFundAmount = toToken.totalSupply() - .mul(exchangeAmount) - .div(smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter)); - + curFundAmount = smartTokenFormula.calculatePurchaseReturn( + smartToken.totalSupply(), + details.tokens[i].token.balanceOf(details.converter), + uint32(details.totalRatio), + exchangeAmount + ); } if (curFundAmount < minFundAmount) { minFundAmount = curFundAmount; } - _infiniteApproveIfNeeded(smartTokenDetails.reserveTokenList[i].token, smartTokenDetails.converter); - + _infiniteApproveIfNeeded(details.tokens[i].token, details.converter); } - ISmartTokenConverter(smartTokenDetails.converter).fund(minFundAmount); + ISmartTokenConverter(details.converter).fund(minFundAmount); dist = new uint256[](distribution.length); dist[2] = 1; // Swap leftovers for SmartToken - for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { - - uint256 tokenBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(address(this)); - - if (tokenBalance == 0) { - continue; - } - - if (!smartTokenDetails.hasHubToken) { - smartTokenDetails.reserveTokenList[i].token.transfer(msg.sender, tokenBalance); - continue; - } - + for (uint i = 0; i < details.tokens.length; i++) { _safeTokenSwap( - smartTokenDetails.reserveTokenList[i].token == originalSUSD - ? susd : smartTokenDetails.reserveTokenList[i].token, - toToken, - tokenBalance, + _canonicalSUSD(details.tokens[i].token), + smartToken, + details.tokens[i].token.balanceOf(address(this)), 0, dist, - FLAG_DISABLE_SMART_TOKEN + FLAG_DISABLE_SMART_TOKEN | FLAG_DISABLE_ALL_WRAPPERS ); - } - } function _safeTokenSwap( @@ -571,6 +494,9 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { uint256[] memory distribution, uint256 disableFlags ) private { + if (amount == 0) { + return; + } (bool successExchange, ) = address(this).call.gas(600000)( abi.encodeWithSelector( @@ -584,11 +510,8 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { ) ); - if (successExchange) { - return; + if (!successExchange) { + fromToken.universalTransfer(msg.sender, amount); } - - fromToken.transfer(msg.sender, amount); } - } diff --git a/test/OneSplit.js b/test/OneSplit.js index cdfec08..178fb1f 100644 --- a/test/OneSplit.js +++ b/test/OneSplit.js @@ -1,4 +1,4 @@ -const { expectRevert } = require('openzeppelin-test-helpers'); +const { expectRevert } = require('@openzeppelin/test-helpers'); const { expect } = require('chai'); const assert = require('assert'); @@ -21,7 +21,7 @@ contract('OneSplit', function ([_, addr1]) { '0x' + (10).toString(16), '0x0', ); - + console.log(res['0'].toString()); console.log(res['1'].map(x => x.toString())); }); @@ -34,7 +34,7 @@ contract('OneSplit', function ([_, addr1]) { '0x' + (10).toString(16), '0x0' ); - + console.log(res['0'].toString()); console.log(res['1'].map(x => x.toString())); }); @@ -132,7 +132,7 @@ contract('OneSplit', function ([_, addr1]) { ); const returnAmount = web3.utils.fromWei(res.returnAmount.toString(), 'ether'); - + console.log(`input: ${inputAmount} ETH`); console.log(`returnAmount: ${returnAmount} bDAI`); console.log('distributionBdai:', res.distribution.map(a => a.toString())); From 04fc62813134d2303e132419dd6e4994be2a1778 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Sun, 5 Apr 2020 01:11:47 +0300 Subject: [PATCH 22/60] Safe Bancor swap added --- contracts/OneSplitBase.sol | 27 +++++++++++++++++++-- contracts/OneSplitSmartToken.sol | 41 +++----------------------------- 2 files changed, 28 insertions(+), 40 deletions(-) diff --git a/contracts/OneSplitBase.sol b/contracts/OneSplitBase.sol index 035cbf9..4efe47f 100644 --- a/contracts/OneSplitBase.sol +++ b/contracts/OneSplitBase.sol @@ -598,6 +598,20 @@ contract OneSplitBase is IOneSplit, OneSplitBaseBase { IERC20 fromToken, IERC20 toToken, uint256 amount + ) internal returns(uint256) { + uint256 ret = _swapOnBancorSafe( + fromToken, + toToken, + amount + ); + require(ret > 0); + return ret; + } + + function _swapOnBancorSafe( + IERC20 fromToken, + IERC20 toToken, + uint256 amount ) internal returns(uint256) { if (fromToken.isETH()) { bancorEtherToken.deposit.value(amount)(); @@ -610,9 +624,18 @@ contract OneSplitBase is IOneSplit, OneSplitBaseBase { ); _infiniteApproveIfNeeded(fromToken.isETH() ? bancorEtherToken : fromToken, address(bancorNetwork)); - uint256 returnAmount = bancorNetwork.claimAndConvert(path, amount, 1); + (bool success, bytes memory data) = address(bancorNetwork).call.gas(1500000)( + abi.encodeWithSelector( + bancorNetwork.claimAndConvert.selector, + path, + amount, + 1 + ) + ); - if (toToken.isETH()) { + uint256 returnAmount = success ? abi.decode(data, (uint256)) : 0; + + if (toToken.isETH() && returnAmount > 0) { bancorEtherToken.withdraw(bancorEtherToken.balanceOf(address(this))); } diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 9afb7bd..5277827 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -398,7 +398,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { dist[j] = (distribution[j] >> (i * 8)) & 0xFF; } - _safeTokenSwap( + this.swap( _canonicalSUSD(details.tokens[i].token), toToken, details.tokens[i].token.balanceOf(address(this)), @@ -470,48 +470,13 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { ISmartTokenConverter(details.converter).fund(minFundAmount); - dist = new uint256[](distribution.length); - dist[2] = 1; - // Swap leftovers for SmartToken for (uint i = 0; i < details.tokens.length; i++) { - _safeTokenSwap( + _swapOnBancorSafe( _canonicalSUSD(details.tokens[i].token), smartToken, - details.tokens[i].token.balanceOf(address(this)), - 0, - dist, - FLAG_DISABLE_SMART_TOKEN | FLAG_DISABLE_ALL_WRAPPERS + details.tokens[i].token.balanceOf(address(this)) ); } } - - function _safeTokenSwap( - IERC20 fromToken, - IERC20 toToken, - uint256 amount, - uint256 minReturnAmount, - uint256[] memory distribution, - uint256 disableFlags - ) private { - if (amount == 0) { - return; - } - - (bool successExchange, ) = address(this).call.gas(600000)( - abi.encodeWithSelector( - this.swap.selector, - fromToken, - toToken, - amount, - minReturnAmount, - distribution, - disableFlags - ) - ); - - if (!successExchange) { - fromToken.universalTransfer(msg.sender, amount); - } - } } From edec249e32d2d78b90e487df542ceafd7a4c1ce7 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Sun, 5 Apr 2020 01:16:30 +0300 Subject: [PATCH 23/60] Fix Bancor safe swap --- contracts/OneSplitSmartToken.sol | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 5277827..37382cf 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -472,11 +472,17 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { // Swap leftovers for SmartToken for (uint i = 0; i < details.tokens.length; i++) { - _swapOnBancorSafe( - _canonicalSUSD(details.tokens[i].token), + IERC20 fromToken = _canonicalSUSD(details.tokens[i].token); + + uint256 ret = _swapOnBancorSafe( + fromToken, smartToken, details.tokens[i].token.balanceOf(address(this)) ); + + if (ret == 0) { + fromToken.universalTransfer(msg.sender, amount); + } } } } From c38acbe697044f6278e64e60e8163b9d263fbf7c Mon Sep 17 00:00:00 2001 From: Kirill Date: Sun, 5 Apr 2020 01:18:37 +0300 Subject: [PATCH 24/60] fix shadowed naming --- contracts/OneSplitSmartToken.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 37382cf..48495c5 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -472,16 +472,16 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { // Swap leftovers for SmartToken for (uint i = 0; i < details.tokens.length; i++) { - IERC20 fromToken = _canonicalSUSD(details.tokens[i].token); + IERC20 reserveToken = _canonicalSUSD(details.tokens[i].token); uint256 ret = _swapOnBancorSafe( - fromToken, + reserveToken, smartToken, details.tokens[i].token.balanceOf(address(this)) ); if (ret == 0) { - fromToken.universalTransfer(msg.sender, amount); + reserveToken.universalTransfer(msg.sender, amount); } } } From 8f7038af66b7f86c8c5905fdc808f3270a6b21ea Mon Sep 17 00:00:00 2001 From: Kirill Date: Sun, 5 Apr 2020 01:35:18 +0300 Subject: [PATCH 25/60] fix --- contracts/OneSplitSmartToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 48495c5..c53d51a 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -277,7 +277,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { _smartToken.totalSupply(), details.tokens[i].token.balanceOf(details.converter), uint32(details.totalRatio), - fundAmounts[i] + _minFundAmount ) ); From f3cd14ca7e8108c072ccf74fb63b1ba7f80ee9f6 Mon Sep 17 00:00:00 2001 From: Kirill Date: Sun, 5 Apr 2020 02:34:21 +0300 Subject: [PATCH 26/60] fixes --- contracts/OneSplitSmartToken.sol | 4 ++-- test/OneSplit.js | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index c53d51a..4764206 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -274,8 +274,8 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { uint256 leftover = tokenAmounts[i].sub( smartTokenFormula.calculateLiquidateReturn( - _smartToken.totalSupply(), - details.tokens[i].token.balanceOf(details.converter), + _smartToken.totalSupply().add(_minFundAmount), + details.tokens[i].token.balanceOf(details.converter).add(tokenAmounts[i]), uint32(details.totalRatio), _minFundAmount ) diff --git a/test/OneSplit.js b/test/OneSplit.js index 178fb1f..7ca742e 100644 --- a/test/OneSplit.js +++ b/test/OneSplit.js @@ -4,20 +4,19 @@ const assert = require('assert'); const OneSplitViewMock = artifacts.require('OneSplitViewMock'); const OneSplitMock = artifacts.require('OneSplitMock'); -const OneSplitSmartTokenView = artifacts.require('OneSplitSmartTokenView'); contract('OneSplit', function ([_, addr1]) { describe('OneSplitSmartContract', async function () { beforeEach('should be ok', async function () { - this.smartTokenView = await OneSplitSmartTokenView.new(); + this.smartTokenView = await OneSplitViewMock.new(); }); it('should view buying price', async function () { const res = await this.smartTokenView.getExpectedReturn( '0x0000000000000000000000000000000000000000', // ETH '0x482c31355F4f7966fFcD38eC5c9635ACAe5F4D4F', // Ether Token Smart Relay Token (ETHUSDB) - '0x' + Number(web3.utils.toWei('20')).toString(16), + '0x' + Number(web3.utils.toWei('0.5')).toString(16), '0x' + (10).toString(16), '0x0', ); From 705a818e44b32ce630179902f6235f3d10436b11 Mon Sep 17 00:00:00 2001 From: Kirill Date: Sun, 5 Apr 2020 17:05:58 +0300 Subject: [PATCH 27/60] fix --- contracts/OneSplitSmartToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 4764206..eef30fe 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -226,7 +226,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); - uint256[] memory tokenAmounts; + uint256[] memory tokenAmounts = new uint256[](details.tokens.length); uint256[] memory dist; uint256[] memory fundAmounts = new uint256[](details.tokens.length); From 25807f454b13d7769f4fb56bcd3144f0f0c509ab Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Mon, 6 Apr 2020 00:04:20 +0300 Subject: [PATCH 28/60] Fix bug --- OneSplit.full.abi | 2 +- OneSplit.full.bin | 2 +- contracts/OneSplitSmartToken.sol | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/OneSplit.full.abi b/OneSplit.full.abi index 94b8183..adf9c53 100644 --- a/OneSplit.full.abi +++ b/OneSplit.full.abi @@ -1 +1 @@ -[{"inputs":[{"internalType":"contract IOneSplitView","name":"_oneSplitView","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[],"name":"ETH_ADDRESS","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BANCOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BDAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_BINANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_SYNTHETIX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_USDT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_Y","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_FULCRUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IEARN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_KYBER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_OASIS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_SMART_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_WETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_BANCOR_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_OASIS_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_UNISWAP_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_DAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_USDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"_isAaveToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"_isFulcrumToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"aave","outputs":[{"internalType":"contract IAaveLendingPool","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorContractRegistry","outputs":[{"internalType":"contract IBancorContractRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorEtherToken","outputs":[{"internalType":"contract IBancorEtherToken","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bdai","outputs":[{"internalType":"contract IBdai","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"btu","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"busd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cETH","outputs":[{"internalType":"contract ICompoundEther","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"chai","outputs":[{"internalType":"contract IChai","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"compound","outputs":[{"internalType":"contract ICompound","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveBinance","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveCompound","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveSynthetix","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveUsdt","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveY","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"getAllRatesForDEX","outputs":[{"internalType":"uint256[]","name":"results","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"getExpectedReturn","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"goodSwap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"kyberNetworkProxy","outputs":[{"internalType":"contract IKyberNetworkProxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oasisExchange","outputs":[{"internalType":"contract IOasisExchange","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oneSplitView","outputs":[{"internalType":"contract IOneSplitView","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"susd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"swap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"tusd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"uniswapFactory","outputs":[{"internalType":"contract IUniswapFactory","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdc","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"wethToken","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file +[{"inputs":[{"internalType":"contract IOneSplitView","name":"_oneSplitView","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[],"name":"ETH_ADDRESS","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_ALL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_ALL_SOURCES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_ALL_WRAPPERS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BANCOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BDAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_BINANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_SYNTHETIX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_USDT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_Y","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_FULCRUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IEARN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_KYBER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_OASIS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_SMART_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_WETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_BANCOR_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_OASIS_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_UNISWAP_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_DAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_USDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"_isAaveToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"_isFulcrumToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"aave","outputs":[{"internalType":"contract IAaveLendingPool","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"acientSUSD","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorContractRegistry","outputs":[{"internalType":"contract IBancorContractRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorEtherToken","outputs":[{"internalType":"contract IBancorEtherToken","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bdai","outputs":[{"internalType":"contract IBdai","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"btu","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"busd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cETH","outputs":[{"internalType":"contract ICompoundEther","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"chai","outputs":[{"internalType":"contract IChai","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"compound","outputs":[{"internalType":"contract ICompound","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveBinance","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveCompound","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveSynthetix","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveUsdt","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveY","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"getAllRatesForDEX","outputs":[{"internalType":"uint256[]","name":"results","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"getExpectedReturn","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"goodSwap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"infiniteApproveIfNeeded","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"kyberNetworkProxy","outputs":[{"internalType":"contract IKyberNetworkProxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oasisExchange","outputs":[{"internalType":"contract IOasisExchange","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oneSplitView","outputs":[{"internalType":"contract IOneSplitView","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"susd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"swap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"swapOnBancorSafe","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tusd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"uniswapFactory","outputs":[{"internalType":"contract IUniswapFactory","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdc","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"wethToken","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/OneSplit.full.bin b/OneSplit.full.bin index d89f14b..43612e9 100644 --- a/OneSplit.full.bin +++ b/OneSplit.full.bin @@ -1 +1 @@ -6080604052600080546001600160a01b0319908116736b175474e89094c44da98b954eedeac495271d0f1790915560018054821673a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4817905560028054821673dac17f958d2ee523a2206206994597c13d831ec71790556003805482166e085d4780b73119b644ae5ecd22b376179055600480548216734fabb145d64652a948d72533023f6e7a623c7c531790556005805482167357ab1ec28d129707052df4df418d58a2d46d5f5117905560068054821673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc217905560078054821673c0829421c1d260bd3cb3e0f06cfe2d52db2ce31517905560088054821673818e6fecd516ecc3849daf6845e3ec868087b75517905560098054821673c0a47dfe034b400b47bdad5fecda2621de6c4d95179055600a805482167352ae12abe5d8bd778bd5397f99ca900624cfadd4179055600b80548216736f0cd8c4f6f06eab664c7e3031909452b4b72861179055600c8054821673794e6e91555438afc3ccf1c5076a74f42133d08d179055600d8054821673a2b47e3d5c44877cca798226b7b8118f9bfb7a56179055600e805482167352ea46506b9cc5ef470c5bf89f17dc28bb35d85c179055600f805482167345f783cce6b7ff23b2ab2d70e416cdb7d6055f511790556010805482167379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27179055601180548216733b12e1fbb468bea80b492d635976809bf950186c1790556012805482167306af07097c9eeb7fd685c692751d5c66db49c215179055601380548216736a4ffaafa8dd400676df8076ad6c724867b0e2e817905560148054821673b683d83a532e2cb7dfa5275eed3698436371cc9f17905560158054821673398ec7346dcd622edc5ae82352f02be94c62d119179055601680548216733d9819210a31b4961b30ef54be2aed79b9c9cd3b179055601780548216734ddc2d193948926d02f9b1fe9e1daa0718270ed517905560188054821673f6e2d7f616b67e46d708e4410746e9aab3a4c5181790556019805490911673524619eb9b4cdffa7da13029b33f24635478afc01790553480156200032457600080fd5b5060405162006a6638038062006a66833981016040819052620003479162000380565b601a80546001600160a01b0319166001600160a01b0392909216919091179055620003e9565b80516200037a81620003cf565b92915050565b6000602082840312156200039357600080fd5b6000620003a184846200036d565b949350505050565b60006200037a82620003c3565b60006200037a82620003a9565b6001600160a01b031690565b620003da81620003b6565b8114620003e657600080fd5b50565b61666d80620003f96000396000f3fe6080604052600436106103355760003560e01c806372b6f1bf116101ab578063c762a46c116100f7578063dc1536b211610095578063f4b9fa751161006f578063f4b9fa75146107c4578063f56e281f146107d9578063f69e2046146107ee578063fbe4ed951461080357610335565b8063dc1536b21461076f578063e2a7515e14610784578063f484966b1461079757610335565b8063c9b42c67116100d1578063c9b42c671461071b578063cede5f6a14610730578063d393c3e914610745578063d77366a41461075a57610335565b8063c762a46c146106dc578063c77b9de6146106f1578063c92577751461070657610335565b80638bdb2afa11610164578063b0a7ef291161013e578063b0a7ef2914610688578063b3bc78441461069d578063b69d0456146106b2578063c11f4f11146106c757610335565b80638bdb2afa14610649578063a1b4d0111461065e578063a734f06e1461067357610335565b806372b6f1bf146105c057806375a8b012146105e057806375b5be2d146105f55780637a88bdbd1461060a578063819faf7b1461061f578063851954fa1461063457610335565b80633e413bee116102855780635aa8fb481161022357806364ec4e5c116101fd57806364ec4e5c1461056e5780636b5a4ca2146105835780636b9589aa146105985780636cbc4a6e146105ab57610335565b80635aa8fb481461052f5780635ae51b82146105445780635c0cb4791461055957610335565b806344211d621161025f57806344211d62146104db5780634a7101d5146104f05780634b57b0be1461050557806351f1985c1461051a57610335565b80633e413bee146104915780634037f967146104a6578063423d03f9146104c657610335565b806322320c98116102f25780632f48ab7d116102cc5780632f48ab7d1461043d57806334b4dabb14610452578063372a26cb146104675780633ca5b2341461047c57610335565b806322320c98146103fe5780632d3b5207146104135780632e707bd21461042857610335565b8063085e2c5b1461034457806312dea1601461037b5780631388b4201461039d57806313989140146103b25780632113240d146103d457806321a360f5146103e9575b3332141561034257600080fd5b005b34801561035057600080fd5b5061036461035f366004615b93565b610818565b6040516103729291906164b9565b60405180910390f35b34801561038757600080fd5b506103906108b8565b604051610372919061620f565b3480156103a957600080fd5b506103906108c7565b3480156103be57600080fd5b506103c76108d6565b60405161037291906164ab565b3480156103e057600080fd5b506103c76108dc565b3480156103f557600080fd5b506103c76108e2565b34801561040a57600080fd5b506103906108eb565b34801561041f57600080fd5b506103c76108fa565b34801561043457600080fd5b506103c7610903565b34801561044957600080fd5b50610390610908565b34801561045e57600080fd5b506103c7610917565b34801561047357600080fd5b5061039061091c565b34801561048857600080fd5b5061039061092b565b34801561049d57600080fd5b5061039061093a565b3480156104b257600080fd5b506103906104c1366004615ab8565b610949565b3480156104d257600080fd5b50610390610c32565b3480156104e757600080fd5b506103c7610c41565b3480156104fc57600080fd5b506103c7610c46565b34801561051157600080fd5b50610390610c4b565b34801561052657600080fd5b50610390610c5a565b34801561053b57600080fd5b506103c7610c69565b34801561055057600080fd5b506103c7610c6f565b34801561056557600080fd5b506103c7610c75565b34801561057a57600080fd5b506103c7610c7a565b34801561058f57600080fd5b50610390610c81565b6103426105a6366004615c08565b610c90565b3480156105b757600080fd5b506103c7610cb9565b3480156105cc57600080fd5b506103906105db366004615ab8565b610cc0565b3480156105ec57600080fd5b506103c7610eaa565b34801561060157600080fd5b50610390610eb0565b34801561061657600080fd5b506103c7610ebf565b34801561062b57600080fd5b50610390610ec4565b34801561064057600080fd5b50610390610ed3565b34801561065557600080fd5b50610390610ee2565b34801561066a57600080fd5b50610390610ef1565b34801561067f57600080fd5b50610390610f00565b34801561069457600080fd5b506103c7610f18565b3480156106a957600080fd5b506103c7610f1e565b3480156106be57600080fd5b50610390610f27565b3480156106d357600080fd5b50610390610f36565b3480156106e857600080fd5b506103c7610f45565b3480156106fd57600080fd5b506103c7610f4a565b34801561071257600080fd5b50610390610f50565b34801561072757600080fd5b506103c7610f5f565b34801561073c57600080fd5b50610390610f66565b34801561075157600080fd5b506103c7610f75565b34801561076657600080fd5b50610390610f7c565b34801561077b57600080fd5b506103c7610f8b565b610342610792366004615af4565b610f91565b3480156107a357600080fd5b506107b76107b2366004615b93565b611058565b60405161037291906161fe565b3480156107d057600080fd5b506103906110ec565b3480156107e557600080fd5b506103c76110fb565b3480156107fa57600080fd5b50610390611100565b34801561080f57600080fd5b5061039061110f565b601a5460405163085e2c5b60e01b81526000916060916001600160a01b039091169063085e2c5b90610856908a908a908a908a908a906004016162c7565b60006040518083038186803b15801561086e57600080fd5b505afa158015610882573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526108aa9190810190615ccb565b915091509550959350505050565b600a546001600160a01b031681565b600c546001600160a01b031681565b61200081565b61800081565b64020000000081565b6011546001600160a01b031681565b64010000000081565b608081565b6002546001600160a01b031681565b604081565b6010546001600160a01b031681565b6004546001600160a01b031681565b6001546001600160a01b031681565b600061095d826001600160a01b031661111e565b1561096b5750600019610c2d565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b17905290516000916060916001600160a01b03861691611388916109b4919061612f565b6000604051808303818686fa925050503d80600081146109f0576040519150601f19603f3d011682016040523d82523d6000602084013e6109f5565b606091505b509150915081610a0b5760001992505050610c2d565b6000805b6007835103811015610b6457828160000181518110610a2a57fe5b6020910101516001600160f81b031916602360f91b148015610a6d5750828160010181518110610a5657fe5b6020910101516001600160f81b031916607560f81b145b8015610a9a5750828160020181518110610a8357fe5b6020910101516001600160f81b031916601b60fa1b145b8015610ac75750828160030181518110610ab057fe5b6020910101516001600160f81b031916606360f81b145b8015610af45750828160040181518110610add57fe5b6020910101516001600160f81b031916603960f91b145b8015610b215750828160050181518110610b0a57fe5b6020910101516001600160f81b031916607560f81b145b8015610b4e5750828160060181518110610b3757fe5b6020910101516001600160f81b031916606d60f81b145b15610b5c5760019150610b64565b600101610a0f565b5080610b77576000199350505050610c2d565b60408051600481526024810182526020810180516001600160e01b031663797bf38560e01b17905290516001600160a01b0387169161138891610bba919061612f565b6000604051808303818686fa925050503d8060008114610bf6576040519150601f19603f3d011682016040523d82523d6000602084013e610bfb565b606091505b50909350915082610c13576000199350505050610c2d565b81806020019051610c279190810190615ad6565b93505050505b919050565b600f546001600160a01b031681565b601081565b602081565b6006546001600160a01b031681565b600d546001600160a01b031681565b61400081565b61080081565b600881565b6202000081565b6014546001600160a01b031681565b6060610c9f8787878686610818565b915050610cb0878787878587610f91565b50505050505050565b6208000081565b6000610cd4826001600160a01b031661111e565b15610ce25750600019610c2d565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b17905290516000916060916001600160a01b0386169161138891610d2b919061612f565b6000604051808303818686fa925050503d8060008114610d67576040519150601f19603f3d011682016040523d82523d6000602084013e610d6c565b606091505b509150915081610d825760001992505050610c2d565b6000805b6004835103811015610e5457828160000181518110610da157fe5b6020910101516001600160f81b031916604160f81b148015610de45750828160010181518110610dcd57fe5b6020910101516001600160f81b031916606160f81b145b8015610e115750828160020181518110610dfa57fe5b6020910101516001600160f81b031916603b60f91b145b8015610e3e5750828160030181518110610e2757fe5b6020910101516001600160f81b031916606560f81b145b15610e4c5760019150610e54565b600101610d86565b5080610e67576000199350505050610c2d565b60408051600481526024810182526020810180516001600160e01b0316632274683f60e21b17905290516001600160a01b0387169161138891610bba919061612f565b61040081565b6003546001600160a01b031681565b600281565b6015546001600160a01b031681565b6007546001600160a01b031681565b6009546001600160a01b031681565b6017546001600160a01b031681565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b6012546001600160a01b031681565b6013546001600160a01b031681565b600181565b61020081565b6005546001600160a01b031681565b6204000081565b600e546001600160a01b031681565b6201000081565b6008546001600160a01b031681565b61010081565b610fac6001600160a01b03871633308763ffffffff61115816565b610fb98686868585611258565b6000610fd46001600160a01b0387163063ffffffff61128b16565b905083811015610fff5760405162461bcd60e51b8152600401610ff69061641e565b60405180910390fd5b6110196001600160a01b038716338363ffffffff61132f16565b5061104e336110376001600160a01b038a163063ffffffff61128b16565b6001600160a01b038a16919063ffffffff61132f16565b5050505050505050565b606082604051908082528060200260200182016040528015611084578160200160208202803883390190505b50905060005b838110156110e2576110c287876110ba876110ae8a6001880163ffffffff6113ac16565b9063ffffffff6113e616565b600187610818565b508282815181106110cf57fe5b602090810291909101015260010161108a565b5095945050505050565b6000546001600160a01b031681565b600481565b6016546001600160a01b031681565b601a546001600160a01b031681565b60006001600160a01b038216158061115257506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b92915050565b8061116257611252565b61116b8461111e565b15611237576001600160a01b038316331480156111885750803410155b6111a45760405162461bcd60e51b8152600401610ff69061644e565b6001600160a01b03821630146111ec576040516001600160a01b0383169082156108fc029083906000818181858888f193505050501580156111ea573d6000803e3d6000fd5b505b8034111561123257336108fc611208348463ffffffff61142816565b6040518115909202916000818181858888f19350505050158015611230573d6000803e3d6000fd5b505b611252565b6112526001600160a01b03851684848463ffffffff61146a16565b50505050565b836001600160a01b0316856001600160a01b0316141561127757611284565b61128485858585856114c5565b5050505050565b60006112968361111e565b156112ac57506001600160a01b03811631611152565b6040516370a0823160e01b81526001600160a01b038416906370a08231906112d890859060040161613b565b60206040518083038186803b1580156112f057600080fd5b505afa158015611304573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506113289190810190615cad565b9392505050565b60008161133e57506001611328565b6113478461111e565b15611388576040516001600160a01b0384169083156108fc029084906000818181858888f19350505050158015611382573d6000803e3d6000fd5b50611328565b6113a26001600160a01b038516848463ffffffff61163016565b5060019392505050565b6000826113bb57506000611152565b828202828482816113c857fe5b04146113285760405162461bcd60e51b8152600401610ff69061645e565b600061132883836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250611657565b600061132883836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061168e565b6040516112529085906323b872dd60e01b9061148e9087908790879060240161619b565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526116ba565b836001600160a01b0316856001600160a01b031614156114e457611284565b6114f68161010063ffffffff61179f16565b611623576018546040516302091f7b60e51b81526001600160a01b0390911690634123ef609061152a90889060040161620f565b60206040518083038186803b15801561154257600080fd5b505afa158015611556573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061157a9190810190615a60565b156115915761158c85858585856117a5565b611284565b6018546040516302091f7b60e51b81526001600160a01b0390911690634123ef60906115c190879060040161620f565b60206040518083038186803b1580156115d957600080fd5b505afa1580156115ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116119190810190615a60565b156116235761158c8585858585611b33565b6112848585858585612136565b60405161165290849063a9059cbb60e01b9061148e90869086906024016161c3565b505050565b600081836116785760405162461bcd60e51b8152600401610ff691906163fd565b50600083858161168457fe5b0495945050505050565b600081848411156116b25760405162461bcd60e51b8152600401610ff691906163fd565b505050900390565b6116cc826001600160a01b0316612143565b6116e85760405162461bcd60e51b8152600401610ff69061649b565b60006060836001600160a01b031683604051611704919061612f565b6000604051808303816000865af19150503d8060008114611741576040519150601f19603f3d011682016040523d82523d6000602084013e611746565b606091505b5091509150816117685760405162461bcd60e51b8152600401610ff69061642e565b80511561125257808060200190516117839190810190615a60565b6112525760405162461bcd60e51b8152600401610ff69061646e565b16151590565b6117ad615748565b6117b68661217f565b905060608160000151516040519080825280602002602001820160405280156117e9578160200160208202803883390190505b509050606082600001515160405190808252806020026020018201604052801561182757816020015b60608152602001906001900390816118125790505b50905060005b83515161ffff821610156119a4578551604051908082528060200260200182016040528015611866578160200160208202803883390190505b50828261ffff168151811061187757fe5b602002602001018190525083600001518161ffff168151811061189657fe5b6020026020010151600001516001600160a01b03166370a08231336040518263ffffffff1660e01b81526004016118cd9190616149565b60206040518083038186803b1580156118e557600080fd5b505afa1580156118f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061191d9190810190615cad565b838261ffff168151811061192d57fe5b602090810291909101015260005b865181101561199b578160080261ffff1687828151811061195857fe5b6020026020010151901c60ff16838361ffff168151811061197557fe5b6020026020010151828151811061198857fe5b602090810291909101015260010161193b565b5060010161182d565b5082602001516001600160a01b031663415f1240876040518263ffffffff1660e01b81526004016119d591906164ab565b600060405180830381600087803b1580156119ef57600080fd5b505af1158015611a03573d6000803e3d6000fd5b505084515160009250159050611b2857600084600001518261ffff1681518110611a2957fe5b6020026020010151600001516001600160a01b03166370a08231336040518263ffffffff1660e01b8152600401611a609190616149565b60206040518083038186803b158015611a7857600080fd5b505afa158015611a8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ab09190810190615cad565b9050611b1e85600001518361ffff1681518110611ac957fe5b6020026020010151600001518a611b00878661ffff1681518110611ae957fe5b60200260200101518561142890919063ffffffff16565b868661ffff1681518110611b1057fe5b60200260200101518a612136565b5050505050611284565b505050505050505050565b600019611b3e615748565b611b478661217f565b90506060816000015151604051908082528060200260200182016040528015611b7a578160200160208202803883390190505b50905060005b82515161ffff82161015611f37576000611bbe8885600001518461ffff1681518110611ba857fe5b6020026020010151602001518660400151612228565b9050600084600001518361ffff1681518110611bd657fe5b6020026020010151600001516001600160a01b03166370a08231336040518263ffffffff1660e01b8152600401611c0d9190616149565b60206040518083038186803b158015611c2557600080fd5b505afa158015611c39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c5d9190810190615cad565b905060005b8851811015611cab578360080261ffff16898281518110611c7f57fe5b6020026020010151901c60ff16898281518110611c9857fe5b6020908102919091010152600101611c62565b50611cd88b86600001518561ffff1681518110611cc457fe5b602002602001015160000151848b8b612136565b600085600001518461ffff1681518110611cee57fe5b6020026020010151600001516001600160a01b03166370a08231336040518263ffffffff1660e01b8152600401611d259190616149565b60206040518083038186803b158015611d3d57600080fd5b505afa158015611d51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d759190810190615cad565b9050611ea986600001518561ffff1681518110611d8e57fe5b6020026020010151600001516001600160a01b03166370a0823188602001516040518263ffffffff1660e01b8152600401611dc9919061613b565b60206040518083038186803b158015611de157600080fd5b505afa158015611df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e199190810190615cad565b6110ae611e2c848663ffffffff61142816565b8e6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611e6557600080fd5b505afa158015611e79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e9d9190810190615cad565b9063ffffffff6113ac16565b858561ffff1681518110611eb957fe5b60200260200101818152505086858561ffff1681518110611ed657fe5b60200260200101511015611eff57848461ffff1681518110611ef457fe5b602002602001015196505b611f2c86600001518561ffff1681518110611f1657fe5b602002602001015160000151876020015161223e565b505050600101611b80565b5081602001516001600160a01b031663ca1d209d846040518263ffffffff1660e01b8152600401611f6891906164ab565b600060405180830381600087803b158015611f8257600080fd5b505af1158015611f96573d6000803e3d6000fd5b506000925050505b82515161ffff82161015611b2857600083600001518261ffff1681518110611fc257fe5b6020026020010151600001516001600160a01b03166370a0823185602001516040518263ffffffff1660e01b8152600401611ffd919061613b565b60206040518083038186803b15801561201557600080fd5b505afa158015612029573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061204d9190810190615cad565b905060006120f38a6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561208d57600080fd5b505afa1580156120a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506120c59190810190615cad565b6110ae84611e9d8a898961ffff16815181106120dd57fe5b602002602001015161142890919063ffffffff16565b9050801561212c5761212a856020015186600001518561ffff168151811061211757fe5b6020026020010151600001518c846122fb565b505b5050600101611f9e565b6112848585858585612497565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061217757508115155b949350505050565b612187615748565b6000826001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156121c257600080fd5b505afa1580156121d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506121fa9190810190615ad6565b90506060600061220983612757565b9085526001600160a01b03909316602085015250506040820152919050565b6000612177826110ae868663ffffffff6113ac16565b612250826001600160a01b031661111e565b6122f757604051636eb1769f60e11b815260ff906001600160a01b0384169063dd62ed3e906122859030908690600401616157565b60206040518083038186803b15801561229d57600080fd5b505afa1580156122b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506122d59190810190615cad565b901c6122f7576122f76001600160a01b0383168260001963ffffffff61293716565b5050565b60006010856001600160a01b03166354fd4d506040518163ffffffff1660e01b815260040160206040518083038186803b15801561233857600080fd5b505afa15801561234c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506123709190810190615c8f565b61ffff1610612409576040516301bafcdb60e61b81526001600160a01b03861690636ebf36c0906123b0908790879087906000908190819060040161626d565b602060405180830381600087803b1580156123ca57600080fd5b505af11580156123de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506124029190810190615cad565b9050612177565b6040516375892cf160e01b81526001600160a01b038616906375892cf19061243c90879087908790600090600401616238565b602060405180830381600087803b15801561245657600080fd5b505af115801561246a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061248e9190810190615cad565b95945050505050565b836001600160a01b0316856001600160a01b031614156124b657611284565b6124c9816208000063ffffffff61179f16565b61274a576006546001600160a01b03868116911614156125da576006546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a082319061251b903090600401616149565b60206040518083038186803b15801561253357600080fd5b505afa158015612547573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061256b9190810190615cad565b6040518263ffffffff1660e01b815260040161258791906164ab565b600060405180830381600087803b1580156125a157600080fd5b505af11580156125b5573d6000803e3d6000fd5b5050505061158c73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee85858585612a0c565b6007546001600160a01b0386811691161415612628576007546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a082319061251b903090600401616149565b6006546001600160a01b03858116911614156126c35761265f8573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585612497565b600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b1580156126af57600080fd5b505af1158015611b1e573d6000803e3d6000fd5b6007546001600160a01b038581169116141561274a576126fa8573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585612497565b600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b1580156126af57600080fd5b6112848585858585612a0c565b60606000826001600160a01b03166371f52bf36040518163ffffffff1660e01b815260040160206040518083038186803b15801561279457600080fd5b505afa1580156127a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127cc9190810190615cad565b60405190808252806020026020018201604052801561280557816020015b6127f2615772565b8152602001906001900390816127ea5790505b50915060005b8251811015612931576040516319b6401560e01b81526001600160a01b038516906319b64015906128409084906004016164ab565b60206040518083038186803b15801561285857600080fd5b505afa15801561286c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506128909190810190615ad6565b83828151811061289c57fe5b6020026020010151600001906001600160a01b031690816001600160a01b0316815250506128e1848483815181106128d057fe5b602002602001015160000151612a19565b8382815181106128ed57fe5b6020026020010151602001818152505061292783828151811061290c57fe5b60200260200101516020015183612b5090919063ffffffff16565b915060010161280b565b50915091565b6129408361111e565b611652576000811180156129d25750604051636eb1769f60e11b81526000906001600160a01b0385169063dd62ed3e90612980903090879060040161618d565b60206040518083038186803b15801561299857600080fd5b505afa1580156129ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506129d09190810190615cad565b115b156129f2576129f26001600160a01b03841683600063ffffffff612b7516565b6116526001600160a01b038416838363ffffffff612b7516565b6112848585858585612c3b565b60006016836001600160a01b03166354fd4d506040518163ffffffff1660e01b815260040160206040518083038186803b158015612a5657600080fd5b505afa158015612a6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612a8e9190810190615c8f565b61ffff1610612ac3576040516316095a2360e11b81526001600160a01b03841690632c12b446906112d890859060040161620f565b60405163359af54960e21b81526001600160a01b0384169063d66bd52490612aef90859060040161613b565b60a06040518083038186803b158015612b0757600080fd5b505afa158015612b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b3f9190810190615c71565b6020015163ffffffff169392505050565b6000828201838110156113285760405162461bcd60e51b8152600401610ff69061640e565b801580612bfd5750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e90612bab903090869060040161618d565b60206040518083038186803b158015612bc357600080fd5b505afa158015612bd7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612bfb9190810190615cad565b155b612c195760405162461bcd60e51b8152600401610ff69061648b565b60405161165290849063095ea7b360e01b9061148e90869086906024016161c3565b836001600160a01b0316856001600160a01b03161415612c5a57611284565b612c62615789565b612c6a613036565b9050612c7e8261080063ffffffff61179f16565b6130215760005b600a811015612e43578181600a8110612c9a57fe5b60200201516001600160a01b0316876001600160a01b03161415612e3b5760008282600a8110612cc657fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015612d0357600080fd5b505afa158015612d17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612d3b9190810190615ad6565b90508282600a8110612d4957fe5b60200201516001600160a01b0316632e1a7d4d876040518263ffffffff1660e01b8152600401612d7991906164ab565b600060405180830381600087803b158015612d9357600080fd5b505af1158015612da7573d6000803e3d6000fd5b50505050612e338188836001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401612ddc9190616149565b60206040518083038186803b158015612df457600080fd5b505afa158015612e08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e2c9190810190615cad565b8888612c3b565b505050611284565b600101612c85565b5060005b600a81101561301f578181600a8110612e5c57fe5b60200201516001600160a01b0316866001600160a01b031614156130175760008282600a8110612e8857fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015612ec557600080fd5b505afa158015612ed9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612efd9190810190615ad6565b9050612f0c8882888888613152565b612f26818484600a8110612f1c57fe5b602002015161223e565b8282600a8110612f3257fe5b60200201516001600160a01b031663b6b55f25826001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401612f719190616149565b60206040518083038186803b158015612f8957600080fd5b505afa158015612f9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612fc19190810190615cad565b6040518263ffffffff1660e01b8152600401612fdd91906164ab565b600060405180830381600087803b158015612ff757600080fd5b505af115801561300b573d6000803e3d6000fd5b50505050505050611284565b600101612e47565b505b61302e8686868686613152565b505050505050565b61303e615789565b5060408051610140810182527316de59092dae5ccf4a1e6439d611fd0653f0bd0181527304aa51bbcb46541455ccf1b8bef2ebc5d3787ec960208201527373a052500105205d34daf004eab301916da8190f918101919091527383f798e925bcd4017eb265844fddabb448f1707d606082015273d6ad7a6750a7593e092a9b218d66c0a814a3436e608082015273f61718057901f84c4eec4339ef8f0d86d2b4560060a08201527304bc0ab673d88ae9dbc9da2380cb6b79c4bca9ae60c082015273c2cb1040220768554cf699b0d863a3cd4324ce3260e082015273e6354ed5bc4b393a5aad09f21c46e101e692d4476101008201527326ea744e5b887e5205727f55dfbe8685e3b2195161012082015290565b61128485858585855b836001600160a01b0316856001600160a01b0316141561317a57611284565b61318b81601063ffffffff61179f16565b6133ad57613198856133ba565b1561325b5760006131a8866134b8565b60405163db006a7560e01b81529091506001600160a01b0387169063db006a75906131d79087906004016164ab565b602060405180830381600087803b1580156131f157600080fd5b505af1158015613205573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506132299190810190615cad565b5060006132456001600160a01b0383163063ffffffff61128b16565b9050613254828783878761315b565b5050611284565b613264846133ba565b156133ad576000613274856134b8565b9050613283868286868661358d565b600061329e6001600160a01b0383163063ffffffff61128b16565b90506132b2826001600160a01b031661111e565b1561332557601760009054906101000a90046001600160a01b03166001600160a01b0316631249c58b826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561330757600080fd5b505af115801561331b573d6000803e3d6000fd5b5050505050613254565b61332f828761223e565b60405163140e25ad60e31b81526001600160a01b0387169063a0712d689061335b9084906004016164ab565b602060405180830381600087803b15801561337557600080fd5b505af1158015613389573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e339190810190615cad565b611284858585858561358d565b6017546000906001600160a01b03838116911614156133db57506001610c2d565b6016546040516000916060916001600160a01b039091169061138890638e8f294b60e01b9061340e90889060240161620f565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161344c919061612f565b6000604051808303818686fa925050503d8060008114613488576040519150601f19603f3d011682016040523d82523d6000602084013e61348d565b606091505b5091509150816134a257600092505050610c2d565b6000818060200190516110e29190810190615a7e565b6017546000906001600160a01b03838116911614156134d957506000610c2d565b60408051600481526024810182526020810180516001600160e01b0316636f307dc360e01b17905290516000916060916001600160a01b0386169161138891613522919061612f565b6000604051808303818686fa925050503d806000811461355e576040519150601f19603f3d011682016040523d82523d6000602084013e613563565b606091505b5091509150816135795760001992505050610c2d565b808060200190516121779190810190615ad6565b6112848585858585836001600160a01b0316856001600160a01b031614156135b457611284565b6135c581602063ffffffff61179f16565b61384b5760006135d486610949565b90506001600160a01b038082161461372f576135f8816001600160a01b031661111e565b156136835760405163081a6b2560e41b81526001600160a01b038716906381a6b2509061362b9030908890600401616172565b602060405180830381600087803b15801561364557600080fd5b505af1158015613659573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061367d9190810190615cad565b50613705565b604051632770a7eb60e21b81526001600160a01b03871690639dc29fac906136b19030908890600401616172565b602060405180830381600087803b1580156136cb57600080fd5b505af11580156136df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506137039190810190615cad565b505b60006137206001600160a01b0383163063ffffffff61128b16565b90506132548287838787613854565b61373885610949565b90506001600160a01b0380821614613849576137578682868686613854565b60006137726001600160a01b0383163063ffffffff61128b16565b9050613786826001600160a01b031661111e565b1561381157604051638f6ede1f60e01b81526001600160a01b03871690638f6ede1f9083906137b9903090600401616149565b6020604051808303818588803b1580156137d257600080fd5b505af11580156137e6573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525061380b9190810190615cad565b50613254565b61381b828761223e565b6040516340c10f1960e01b81526001600160a01b038716906340c10f199061335b9030908590600401616172565b505b61128485858585855b61128485858585855b836001600160a01b0316856001600160a01b0316141561387c57611284565b61388d81608063ffffffff61179f16565b613a8f57600061389c86610cc0565b90506001600160a01b038082161461391f5760405163db006a7560e01b81526001600160a01b0387169063db006a75906138da9087906004016164ab565b600060405180830381600087803b1580156138f457600080fd5b505af1158015613908573d6000803e3d6000fd5b50505050613919818686868661385d565b50611284565b61392885610cc0565b90506001600160a01b0380821614613a8d576139478682868686613a98565b60006139626001600160a01b0383163063ffffffff61128b16565b90506139f382601560009054906101000a90046001600160a01b03166001600160a01b031663f2f4eb266040518163ffffffff1660e01b815260040160206040518083038186803b1580156139b657600080fd5b505afa1580156139ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506139ee9190810190615a0d565b61223e565b6015546001600160a01b039081169063d2d0e06690613a1390851661111e565b613a1e576000613a20565b825b613a32856001600160a01b031661111e565b613a3c5784613a52565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b8461044d6040518563ffffffff1660e01b8152600401613a74939291906163ac565b6000604051808303818588803b158015612ff757600080fd5b505b61128485858585855b836001600160a01b0316856001600160a01b03161415613ab757611284565b613ac98161040063ffffffff61179f16565b613d4f576013546001600160a01b0386811691161415613c2557601354604051637f8661a160e01b81526001600160a01b0390911690637f8661a190613b139086906004016164ab565b600060405180830381600087803b158015613b2d57600080fd5b505af1158015613b41573d6000803e3d6000fd5b50506014546040516370a0823160e01b8152600093506001600160a01b0390911691506370a0823190613b78903090600401616149565b60206040518083038186803b158015613b9057600080fd5b505afa158015613ba4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613bc89190810190615cad565b90508015613c0c57601454606090613bed906001600160a01b03168784600187610818565b601454909250613c0a91506001600160a01b031687848487611258565b505b600054613919906001600160a01b031686868686613d58565b6013546001600160a01b0385811691161415613d4f57600054613c559086906001600160a01b0316858585613d58565b600054601354613c71916001600160a01b03908116911661223e565b6013546000546040516370a0823160e01b81526001600160a01b039283169263049878f39216906370a0823190613cac903090600401616149565b60206040518083038186803b158015613cc457600080fd5b505afa158015613cd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613cfc9190810190615cad565b6040518263ffffffff1660e01b8152600401613d1891906164ab565b600060405180830381600087803b158015613d3257600080fd5b505af1158015613d46573d6000803e3d6000fd5b50505050611284565b61128485858585855b836001600160a01b0316856001600160a01b03161415613d7757611284565b613d8881604063ffffffff61179f16565b613f8c576012546001600160a01b0386811691161415613e955760125460405163ef693bed60e01b81526001600160a01b039091169063ef693bed90613dd49030908790600401616172565b600060405180830381600087803b158015613dee57600080fd5b505af1158015613e02573d6000803e3d6000fd5b50506000546040516370a0823160e01b815261158c93506001600160a01b039091169150869082906370a0823190613e3e903090600401616149565b60206040518083038186803b158015613e5657600080fd5b505afa158015613e6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613e8e9190810190615cad565b8585613f95565b6012546001600160a01b0385811691161415613f8c57600054613ec59086906001600160a01b0316858585613f95565b600054601254613ee1916001600160a01b03908116911661223e565b6012546000546040516370a0823160e01b81526001600160a01b0392831692633b4da69f9230929116906370a0823190613f1f908490600401616149565b60206040518083038186803b158015613f3757600080fd5b505afa158015613f4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613f6f9190810190615cad565b6040518363ffffffff1660e01b8152600401613d18929190616172565b61128485858585855b613fa7856001600160a01b031661111e565b158015613fc35750613fc1846001600160a01b031661111e565b155b8015613fdc5750613fdc8161020063ffffffff61179f16565b156140dd576060825160405190808252806020026020018201604052801561400e578160200160208202803883390190505b50905060005b83518110156140535783818151811061402957fe5b602002602001015160ff1682828151811061404057fe5b6020908102919091010152600101614014565b506140758673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8684866143e7565b60005b83518110156140bb57600884828151811061408f57fe5b6020026020010151901c60ff168282815181106140a857fe5b6020908102919091010152600101614078565b5061391973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee864784866143e7565b6000546001600160a01b0386811691161480159061410957506000546001600160a01b03858116911614155b80156141235750614123816201000063ffffffff61179f16565b156142895760608251604051908082528060200260200182016040528015614155578160200160208202803883390190505b50905060005b835181101561419a5783818151811061417057fe5b602002602001015160ff1682828151811061418757fe5b602090810291909101015260010161415b565b506000546141b59087906001600160a01b03168684866143e7565b60005b83518110156141fb5760088482815181106141cf57fe5b6020026020010151901c60ff168282815181106141e857fe5b60209081029190910101526001016141b8565b506000546040516370a0823160e01b8152613919916001600160a01b031690879082906370a0823190614232903090600401616149565b60206040518083038186803b15801561424a57600080fd5b505afa15801561425e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506142829190810190615cad565b84866143e7565b6001546001600160a01b038681169116148015906142b557506001546001600160a01b03858116911614155b80156142cf57506142cf816202000063ffffffff61179f16565b156143de5760608251604051908082528060200260200182016040528015614301578160200160208202803883390190505b50905060005b83518110156143465783818151811061431c57fe5b602002602001015160ff1682828151811061433357fe5b6020908102919091010152600101614307565b506001546143619087906001600160a01b03168684866143e7565b60005b83518110156143a757600884828151811061437b57fe5b6020026020010151901c60ff1682828151811061439457fe5b6020908102919091010152600101614364565b506001546040516370a0823160e01b8152613919916001600160a01b031690879082906370a0823190614232903090600401616149565b61128485858585855b836001600160a01b0316856001600160a01b0316141561440657611284565b61440e6157a8565b50604080516101208101825261458381526147f7602082015261493491810191909152614cc16060820152614f50608082015261509760a08201526151e660c082015261538860e082015261552a610100820152600080805b60098110156144c057600086828151811061447e57fe5b602002602001015111156144b8576144b286828151811061449b57fe5b602002602001015184612b5090919063ffffffff16565b92508091505b600101614467565b50600082116144e15760405162461bcd60e51b8152600401610ff69061643e565b8560005b6009811015614577578681815181106144fa57fe5b60200260200101516000141561450f5761456f565b600061453b856110ae8a858151811061452457fe5b60200260200101518c6113ac90919063ffffffff16565b9050838214156145485750815b808303925061456c8b8b8389866009811061455f57fe5b602002015163ffffffff16565b50505b6001016144e5565b50505050505050505050565b6000816145986001600160a01b03861661111e565b6146c0576009546040516303795fb160e11b81526000916001600160a01b0316906306f2bf62906145cd90899060040161620f565b60206040518083038186803b1580156145e557600080fd5b505afa1580156145f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061461d9190810190615ad6565b90506001600160a01b038116156146be57614638868261223e565b6040516395e3c50b60e01b81526001600160a01b038216906395e3c50b9061466990859060019042906004016164d9565b602060405180830381600087803b15801561468357600080fd5b505af1158015614697573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506146bb9190810190615cad565b91505b505b6146d2846001600160a01b031661111e565b612177576009546040516303795fb160e11b81526000916001600160a01b0316906306f2bf629061470790889060040161620f565b60206040518083038186803b15801561471f57600080fd5b505afa158015614733573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506147579190810190615ad6565b90506001600160a01b038116156147ee5760405163f39b5b9b60e01b81526001600160a01b0382169063f39b5b9b9084906147999060019042906004016163ef565b6020604051808303818588803b1580156147b257600080fd5b505af11580156147c6573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506147eb9190810190615cad565b91505b50949350505050565b6008546000906148119085906001600160a01b031661223e565b6008546001600160a01b03908116906329589f619061483190871661111e565b61483c57600061483e565b835b614850876001600160a01b031661111e565b61485a5786614870565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b85614883886001600160a01b031661111e565b61488d57876148a3565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b30600160ff1b6000734d37f28d2db99e8d35a6c725a5f1749a085850a36040518963ffffffff1660e01b81526004016148e29796959493929190616309565b6020604051808303818588803b1580156148fb57600080fd5b505af115801561490f573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506121779190810190615cad565b6000614948846001600160a01b031661111e565b156149b757600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561499d57600080fd5b505af11580156149b1573d6000803e3d6000fd5b50505050505b600a54604051632ecd14d360e21b81526000916001600160a01b03169063bb34534c906149e69060040161647e565b60206040518083038186803b1580156149fe57600080fd5b505afa158015614a12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614a369190810190615a0d565b600b549091506060906001600160a01b03908116906375e1cc8290614a5c90891661111e565b614a665787614a73565b6007546001600160a01b03165b614a85886001600160a01b031661111e565b614a8f5787614a9c565b6007546001600160a01b03165b6040518363ffffffff1660e01b8152600401614ab992919061621d565b60006040518083038186803b158015614ad157600080fd5b505afa158015614ae5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052614b0d9190810190615a2b565b9050614b41614b24876001600160a01b031661111e565b614b2e5786614b3b565b6007546001600160a01b03165b8361223e565b6040516331ee892f60e21b81526000906001600160a01b0384169063c7ba24bc90614b7590859089906001906004016161d1565b602060405180830381600087803b158015614b8f57600080fd5b505af1158015614ba3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614bc79190810190615cad565b9050614bdb866001600160a01b031661111e565b15614cb7576007546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a0823190614c18903090600401616149565b60206040518083038186803b158015614c3057600080fd5b505afa158015614c44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614c689190810190615cad565b6040518263ffffffff1660e01b8152600401614c8491906164ab565b600060405180830381600087803b158015614c9e57600080fd5b505af1158015614cb2573d6000803e3d6000fd5b505050505b9695505050505050565b6000614cd5846001600160a01b031661111e565b15614d4457600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015614d2a57600080fd5b505af1158015614d3e573d6000803e3d6000fd5b50505050505b614d81614d59856001600160a01b031661111e565b614d635784614d70565b6006546001600160a01b03165b600c546001600160a01b031661223e565b600c546000906001600160a01b0390811690630621b4f690614da490881661111e565b614dae5786614dbb565b6006546001600160a01b03165b85614dce886001600160a01b031661111e565b614dd85787614de5565b6006546001600160a01b03165b60016040518563ffffffff1660e01b8152600401614e069493929190616384565b602060405180830381600087803b158015614e2057600080fd5b505af1158015614e34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614e589190810190615cad565b9050614e6c846001600160a01b031661111e565b15612177576006546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a0823190614ea9903090600401616149565b60206040518083038186803b158015614ec157600080fd5b505afa158015614ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614ef99190810190615cad565b6040518263ffffffff1660e01b8152600401614f1591906164ab565b600060405180830381600087803b158015614f2f57600080fd5b505af1158015614f43573d6000803e3d6000fd5b5050505090509392505050565b60015460009081906001600160a01b03868116911614614f71576000614f74565b60025b6000546001600160a01b03878116911614614f90576000614f93565b60015b600154910160ff1691506000906001600160a01b0390811690861614614fba576000614fbd565b60025b6000546001600160a01b03878116911614614fd9576000614fdc565b60015b0160ff16905081600f0b60001480614ff7575080600f0b6000145b1561500757600092505050611328565b600d5461501e9087906001600160a01b031661223e565b600d54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b600060405180830381600087803b15801561507657600080fd5b505af115801561508a573d6000803e3d6000fd5b5050505050509392505050565b60025460009081906001600160a01b038681169116146150b85760006150bb565b60035b6001546001600160a01b038781169116146150d75760006150da565b60025b6000546001600160a01b038881169116146150f65760006150f9565b60015b60025491019190910160ff1691506000906001600160a01b0390811690861614615124576000615127565b60035b6001546001600160a01b03878116911614615143576000615146565b60025b6000546001600160a01b03888116911614615162576000615165565b60015b010160ff16905081600f0b60001480615181575080600f0b6000145b1561519157600092505050611328565b600e546151a89087906001600160a01b031661223e565b600e54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b60035460009081906001600160a01b0386811691161461520757600061520a565b60045b6002546001600160a01b03878116911614615226576000615229565b60035b6001546001600160a01b03888116911614615245576000615248565b60025b6000546001600160a01b03898116911614615264576000615267565b60015b01010160ff1690506000600360009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b0316146152a65760006152a9565b60045b6002546001600160a01b038781169116146152c55760006152c8565b60035b6001546001600160a01b038881169116146152e45760006152e7565b60025b6000546001600160a01b03898116911614615303576000615306565b60015b01010160ff16905081600f0b60001480615323575080600f0b6000145b1561533357600092505050611328565b600f5461534a9087906001600160a01b031661223e565b600f54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b60045460009081906001600160a01b038681169116146153a95760006153ac565b60045b6002546001600160a01b038781169116146153c85760006153cb565b60035b6001546001600160a01b038881169116146153e75760006153ea565b60025b6000546001600160a01b03898116911614615406576000615409565b60015b01010160ff1690506000600460009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b03161461544857600061544b565b60045b6002546001600160a01b0387811691161461546757600061546a565b60035b6001546001600160a01b03888116911614615486576000615489565b60025b6000546001600160a01b038981169116146154a55760006154a8565b60015b01010160ff16905081600f0b600014806154c5575080600f0b6000145b156154d557600092505050611328565b6010546154ec9087906001600160a01b031661223e565b601054604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b60055460009081906001600160a01b0386811691161461554b57600061554e565b60055b6003546001600160a01b0387811691161461556a57600061556d565b60045b6002546001600160a01b0388811691161461558957600061558c565b60035b6001546001600160a01b038981169116146155a85760006155ab565b60025b6000546001600160a01b038a81169116146155c75760006155ca565b60015b0101010160ff1690506000600560009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b03161461560a57600061560d565b60055b6003546001600160a01b0387811691161461562957600061562c565b60045b6002546001600160a01b0388811691161461564857600061564b565b60035b6001546001600160a01b0389811691161461566757600061566a565b60025b6000546001600160a01b038a8116911614615686576000615689565b60015b0101010160ff16905081600f0b600014806156a7575080600f0b6000145b156156b757600092505050611328565b6005546001600160a01b038781169116148015906156e357506005546001600160a01b03868116911614155b156156f357600092505050611328565b60115461570a9087906001600160a01b031661223e565b601154604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b60405180606001604052806060815260200160006001600160a01b03168152602001600081525090565b604080518082019091526000808252602082015290565b604051806101400160405280600a906020820280388339509192915050565b6040518061012001604052806009905b6157d18152602001906001900390816157b85790505090565bfe5b8051611152816165e6565b600082601f8301126157ef57600080fd5b81516158026157fd8261651b565b6164f4565b9150818183526020840193506020810190508385602084028201111561582757600080fd5b60005b83811015615853578161583d88826157d3565b845250602092830192919091019060010161582a565b5050505092915050565b600082601f83011261586e57600080fd5b813561587c6157fd8261651b565b915081818352602084019350602081019050838560208402820111156158a157600080fd5b60005b8381101561585357816158b788826159ec565b84525060209283019291909101906001016158a4565b600082601f8301126158de57600080fd5b81516158ec6157fd8261651b565b9150818183526020840193506020810190508385602084028201111561591157600080fd5b60005b83811015615853578161592788826159f7565b8452506020928301929190910190600101615914565b8051611152816165fd565b803561115281616606565b805161115281616606565b600060a0828403121561597057600080fd5b61597a60a06164f4565b9050600061598884846159f7565b825250602061599984848301615a02565b60208301525060406159ad8482850161593d565b60408301525060606159c18482850161593d565b60608301525060806159d58482850161593d565b60808301525092915050565b80516111528161660f565b803561115281616618565b805161115281616618565b805161115281616621565b600060208284031215615a1f57600080fd5b600061217784846157d3565b600060208284031215615a3d57600080fd5b815167ffffffffffffffff811115615a5457600080fd5b612177848285016157de565b600060208284031215615a7257600080fd5b6000612177848461593d565b60008060408385031215615a9157600080fd5b6000615a9d858561593d565b9250506020615aae858286016159f7565b9150509250929050565b600060208284031215615aca57600080fd5b60006121778484615948565b600060208284031215615ae857600080fd5b60006121778484615953565b60008060008060008060c08789031215615b0d57600080fd5b6000615b198989615948565b9650506020615b2a89828a01615948565b9550506040615b3b89828a016159ec565b9450506060615b4c89828a016159ec565b935050608087013567ffffffffffffffff811115615b6957600080fd5b615b7589828a0161585d565b92505060a0615b8689828a016159ec565b9150509295509295509295565b600080600080600060a08688031215615bab57600080fd5b6000615bb78888615948565b9550506020615bc888828901615948565b9450506040615bd9888289016159ec565b9350506060615bea888289016159ec565b9250506080615bfb888289016159ec565b9150509295509295909350565b60008060008060008060c08789031215615c2157600080fd5b6000615c2d8989615948565b9650506020615c3e89828a01615948565b9550506040615c4f89828a016159ec565b9450506060615c6089828a016159ec565b9350506080615b7589828a016159ec565b600060a08284031215615c8357600080fd5b6000612177848461595e565b600060208284031215615ca157600080fd5b600061217784846159e1565b600060208284031215615cbf57600080fd5b600061217784846159f7565b60008060408385031215615cde57600080fd5b6000615cea85856159f7565b925050602083015167ffffffffffffffff811115615d0757600080fd5b615aae858286016158cd565b6000615d1f8383615d42565b505060200190565b6000615d1f8383616126565b615d3c8161658f565b82525050565b615d3c8161654f565b6000615d5682616542565b615d608185616546565b9350615d6b8361653c565b8060005b83811015615d99578151615d838882615d13565b9750615d8e8361653c565b925050600101615d6f565b509495945050505050565b6000615daf82616542565b615db98185616546565b9350615dc48361653c565b8060005b83811015615d99578151615ddc8882615d27565b9750615de78361653c565b925050600101615dc8565b6000615dfd82616542565b615e078185610c2d565b9350615e178185602086016165b0565b9290920192915050565b615d3c8161655f565b615d3c8161656a565b615d3c8161659a565b615d3c816165a5565b6000615e5082616542565b615e5a8185616546565b9350615e6a8185602086016165b0565b615e73816165dc565b9093019392505050565b6000615e8a601b83616546565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b6000615ec3603583616546565b7f4f6e6553706c69743a2061637475616c2072657475726e20616d6f756e74206981527439903632b9b9903a3430b71036b4b72932ba3ab93760591b602082015260400192915050565b6000615f1a602083616546565b7f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815260200192915050565b6000615f53602f83616546565b7f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f81526e6e7461696e206e6f6e2d7a65726f7360881b602082015260400192915050565b6000615fa4602b83616546565b7f57726f6e6720757365616765206f66204554482e756e6976657273616c54726181526a6e7366657246726f6d282960a81b602082015260400192915050565b6000615ff1602183616546565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000611152600083616546565b6000616041602a83616546565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e8152691bdd081cdd58d8d9595960b21b602082015260400192915050565b6c42616e636f724e6574776f726b60981b9052565b60006160a2603683616546565b7f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f81527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b602082015260400192915050565b60006160fa601f83616546565b7f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400815260200192915050565b615d3c81616583565b60006113288284615df2565b602081016111528284615d42565b602081016111528284615d33565b604081016161658285615d33565b6113286020830184615d42565b604081016161808285615d33565b6113286020830184616126565b604081016161658285615d42565b606081016161a98286615d42565b6161b66020830185615d42565b6121776040830184616126565b604081016161808285615d42565b606080825281016161e28186615d4b565b90506161f16020830185616126565b6121776040830184615e33565b602080825281016113288184615da4565b602081016111528284615e21565b6040810161622b8285615e21565b6113286020830184615e21565b608081016162468287615e21565b6162536020830186615e21565b6162606040830185616126565b61248e6060830184615e33565b60c0810161627b8289615e21565b6162886020830188615e21565b6162956040830187616126565b6162a26060830186615e33565b6162af6080830185615d33565b6162bc60a0830184615e33565b979650505050505050565b60a081016162d58288615e21565b6162e26020830187615e21565b6162ef6040830186616126565b6162fc6060830185616126565b614cb76080830184616126565b6101008101616318828a615e21565b6163256020830189616126565b6163326040830188615e21565b61633f6060830187615d33565b61634c6080830186615e33565b61635960a0830185615e33565b61636660c0830184615d33565b81810360e083015261637781616027565b9998505050505050505050565b608081016163928287615e21565b61639f6020830186616126565b6162606040830185615e21565b606081016163ba8286615e21565b6163c76020830185616126565b6121776040830184615e3c565b608081016163e28287615e2a565b6162536020830186615e2a565b604081016161808285615e33565b602080825281016113288184615e45565b6020808252810161115281615e7d565b6020808252810161115281615eb6565b6020808252810161115281615f0d565b6020808252810161115281615f46565b6020808252810161115281615f97565b6020808252810161115281615fe4565b6020808252810161115281616034565b60208101610c2d82616080565b6020808252810161115281616095565b60208082528101611152816160ed565b602081016111528284616126565b604081016164c78285616126565b81810360208301526121778184615da4565b606081016164e78286616126565b6161b66020830185615e33565b60405181810167ffffffffffffffff8111828210171561651357600080fd5b604052919050565b600067ffffffffffffffff82111561653257600080fd5b5060209081020190565b60200190565b5190565b90815260200190565b600061115282616577565b151590565b60006111528261654f565b600f0b90565b61ffff1690565b6001600160a01b031690565b90565b63ffffffff1690565b60006111528261655f565b600061115282616583565b600061115282616570565b60005b838110156165cb5781810151838201526020016165b3565b838111156112525750506000910152565b601f01601f191690565b6165ef8161654f565b81146165fa57600080fd5b50565b6165ef8161655a565b6165ef8161655f565b6165ef81616570565b6165ef81616583565b6165ef8161658656fea365627a7a723158208b80610e5ccc7644892fc0a33e2a10b10d27b8f595dfa293f6f52c6a14cce8f66c6578706572696d656e74616cf564736f6c63430005110040 \ No newline at end of file +6080604052600080546001600160a01b0319908116736b175474e89094c44da98b954eedeac495271d0f1790915560018054821673a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4817905560028054821673dac17f958d2ee523a2206206994597c13d831ec71790556003805482166e085d4780b73119b644ae5ecd22b376179055600480548216734fabb145d64652a948d72533023f6e7a623c7c531790556005805482167357ab1ec28d129707052df4df418d58a2d46d5f5190811790915560068054831673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc217905560078054831673c0829421c1d260bd3cb3e0f06cfe2d52db2ce31517905560088054831673818e6fecd516ecc3849daf6845e3ec868087b75517905560098054831673c0a47dfe034b400b47bdad5fecda2621de6c4d95179055600a805483167352ae12abe5d8bd778bd5397f99ca900624cfadd4179055600b80548316736f0cd8c4f6f06eab664c7e3031909452b4b72861179055600c8054831673794e6e91555438afc3ccf1c5076a74f42133d08d179055600d8054831673a2b47e3d5c44877cca798226b7b8118f9bfb7a56179055600e805483167352ea46506b9cc5ef470c5bf89f17dc28bb35d85c179055600f805483167345f783cce6b7ff23b2ab2d70e416cdb7d6055f511790556010805483167379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27179055601180548316733b12e1fbb468bea80b492d635976809bf950186c1790556012805483167306af07097c9eeb7fd685c692751d5c66db49c215179055601380548316736a4ffaafa8dd400676df8076ad6c724867b0e2e817905560148054831673b683d83a532e2cb7dfa5275eed3698436371cc9f17905560158054831673398ec7346dcd622edc5ae82352f02be94c62d119179055601680548316733d9819210a31b4961b30ef54be2aed79b9c9cd3b179055601780548316734ddc2d193948926d02f9b1fe9e1daa0718270ed517905560188054831673f6e2d7f616b67e46d708e4410746e9aab3a4c51817905560198054831673524619eb9b4cdffa7da13029b33f24635478afc0179055601a80548316731f573d6fb3f13d689ff844b4ce37794d79a7ff1c179055601b8054831673309627af60f0926daa6041b8279484312f2bf060179055601c805483169091179055601d80549091167357ab1e02fee23774580c119740129eac7081e9d31790553480156200038c57600080fd5b5060405162006a6638038062006a6683398181016040526020811015620003b257600080fd5b5051601e80546001600160a01b0319166001600160a01b0390921691909117905561668380620003e36000396000f3fe6080604052600436106103975760003560e01c806375a8b012116101dc578063c762a46c11610102578063d77366a4116100a0578063f4b9fa751161006f578063f4b9fa7514610b01578063f56e281f14610b16578063f69e204614610b2b578063fbe4ed9514610b4057610397565b8063d77366a414610970578063dc1536b214610985578063e2a7515e1461099a578063f484966b14610a6257610397565b8063c9b27359116100dc578063c9b27359146108f6578063c9b42c6714610931578063cede5f6a14610946578063d393c3e91461095b57610397565b8063c762a46c146108b7578063c77b9de6146108cc578063c9257775146108e157610397565b80639be685f01161017a578063b3bc784411610149578063b3bc784414610863578063b69d045614610878578063c11f4f111461088d578063c2ea9fb0146108a257610397565b80639be685f0146107e1578063a1b4d01114610824578063a734f06e14610839578063b0a7ef291461084e57610397565b8063819faf7b116101b6578063819faf7b1461078d578063851954fa146107a25780638bdb2afa146107b757806390fd1c9b146107cc57610397565b806375a8b0121461074e57806375b5be2d146107635780637a88bdbd1461077857610397565b80633e413bee116102c15780635aa8fb481161025f5780636b5a4ca21161022e5780636b5a4ca2146106a95780636b9589aa146106be5780636cbc4a6e1461070657806372b6f1bf1461071b57610397565b80635aa8fb48146106555780635ae51b821461066a5780635c0cb4791461067f57806364ec4e5c1461069457610397565b806344211d621161029b57806344211d62146106015780634a7101d5146106165780634b57b0be1461062b57806351f1985c1461064057610397565b80633e413bee146105a45780634037f967146105b9578063423d03f9146105ec57610397565b806323c2257c116103395780632f48ab7d116103085780632f48ab7d1461055057806334b4dabb14610565578063372a26cb1461057a5780633ca5b2341461058f57610397565b806323c2257c146104fc57806325636385146105115780632d3b5207146105265780632e707bd21461053b57610397565b8063139891401161037557806313989140146104965780632113240d146104bd57806321a360f5146104d257806322320c98146104e757610397565b8063085e2c5b146103a657806312dea160146104505780631388b42014610481575b333214156103a457600080fd5b005b3480156103b257600080fd5b506103f5600480360360a08110156103c957600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060800135610b55565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b8381101561043b578181015183820152602001610423565b50505050905001935050505060405180910390f35b34801561045c57600080fd5b50610465610c9d565b604080516001600160a01b039092168252519081900360200190f35b34801561048d57600080fd5b50610465610cac565b3480156104a257600080fd5b506104ab610cbb565b60408051918252519081900360200190f35b3480156104c957600080fd5b506104ab610cc1565b3480156104de57600080fd5b506104ab610cc7565b3480156104f357600080fd5b50610465610cd0565b34801561050857600080fd5b50610465610cdf565b34801561051d57600080fd5b506104ab610cee565b34801561053257600080fd5b506104ab610cf5565b34801561054757600080fd5b506104ab610cfd565b34801561055c57600080fd5b50610465610d02565b34801561057157600080fd5b506104ab610d11565b34801561058657600080fd5b50610465610d16565b34801561059b57600080fd5b50610465610d25565b3480156105b057600080fd5b50610465610d34565b3480156105c557600080fd5b50610465600480360360208110156105dc57600080fd5b50356001600160a01b0316610d43565b3480156105f857600080fd5b506104656110bb565b34801561060d57600080fd5b506104ab6110ca565b34801561062257600080fd5b506104ab6110cf565b34801561063757600080fd5b506104656110d4565b34801561064c57600080fd5b506104656110e3565b34801561066157600080fd5b506104ab6110f2565b34801561067657600080fd5b506104ab6110f8565b34801561068b57600080fd5b506104ab6110fe565b3480156106a057600080fd5b506104ab611103565b3480156106b557600080fd5b5061046561110a565b6103a4600480360360c08110156106d457600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060808101359060a00135611119565b34801561071257600080fd5b506104ab611142565b34801561072757600080fd5b506104656004803603602081101561073e57600080fd5b50356001600160a01b0316611149565b34801561075a57600080fd5b506104ab611399565b34801561076f57600080fd5b5061046561139f565b34801561078457600080fd5b506104ab6113ae565b34801561079957600080fd5b506104656113b3565b3480156107ae57600080fd5b506104656113c2565b3480156107c357600080fd5b506104656113d1565b3480156107d857600080fd5b506104ab6113e0565b3480156107ed57600080fd5b506104ab6004803603606081101561080457600080fd5b506001600160a01b038135811691602081013590911690604001356113e7565b34801561083057600080fd5b5061046561140a565b34801561084557600080fd5b50610465611419565b34801561085a57600080fd5b506104ab611431565b34801561086f57600080fd5b506104ab611437565b34801561088457600080fd5b50610465611440565b34801561089957600080fd5b5061046561144f565b3480156108ae57600080fd5b506104ab61145e565b3480156108c357600080fd5b506104ab611465565b3480156108d857600080fd5b506104ab61146a565b3480156108ed57600080fd5b50610465611470565b34801561090257600080fd5b506103a46004803603604081101561091957600080fd5b506001600160a01b038135811691602001351661147f565b34801561093d57600080fd5b506104ab611499565b34801561095257600080fd5b506104656114a0565b34801561096757600080fd5b506104ab6114af565b34801561097c57600080fd5b506104656114b6565b34801561099157600080fd5b506104ab6114c5565b6103a4600480360360c08110156109b057600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135600160201b8111156109ef57600080fd5b820183602082011115610a0157600080fd5b803590602001918460208302840111600160201b83111715610a2257600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050913592506114cb915050565b348015610a6e57600080fd5b50610ab1600480360360a0811015610a8557600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608001356115b6565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610aed578181015183820152602001610ad5565b505050509050019250505060405180910390f35b348015610b0d57600080fd5b5061046561164a565b348015610b2257600080fd5b506104ab611659565b348015610b3757600080fd5b5061046561165e565b348015610b4c57600080fd5b5061046561166d565b601e546040805163085e2c5b60e01b81526001600160a01b03888116600483015287811660248301526044820187905260648201869052608482018590529151600093606093169163085e2c5b9160a48083019287929190829003018186803b158015610bc157600080fd5b505afa158015610bd5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610bfe57600080fd5b815160208301805160405192949293830192919084600160201b821115610c2457600080fd5b908301906020820185811115610c3957600080fd5b82518660208202830111600160201b82111715610c5557600080fd5b82525081516020918201928201910280838360005b83811015610c82578181015183820152602001610c6a565b50505050905001604052505050915091509550959350505050565b600a546001600160a01b031681565b600c546001600160a01b031681565b61200081565b61800081565b64020000000081565b6011546001600160a01b031681565b601d546001600160a01b031681565b6204f00f81565b600160201b81565b608081565b6002546001600160a01b031681565b604081565b6010546001600160a01b031681565b6004546001600160a01b031681565b6001546001600160a01b031681565b6000610d57826001600160a01b031661167c565b15610d6557506000196110b6565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b178152915181516000936060936001600160a01b0388169361138893919290918291908083835b60208310610dd05780518252601f199092019160209182019101610db1565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114610e31576040519150601f19603f3d011682016040523d82523d6000602084013e610e36565b606091505b509150915081610e4c57600019925050506110b6565b6000805b6007835103811015610fa557828160000181518110610e6b57fe5b6020910101516001600160f81b031916602360f91b148015610eae5750828160010181518110610e9757fe5b6020910101516001600160f81b031916607560f81b145b8015610edb5750828160020181518110610ec457fe5b6020910101516001600160f81b031916601b60fa1b145b8015610f085750828160030181518110610ef157fe5b6020910101516001600160f81b031916606360f81b145b8015610f355750828160040181518110610f1e57fe5b6020910101516001600160f81b031916603960f91b145b8015610f625750828160050181518110610f4b57fe5b6020910101516001600160f81b031916607560f81b145b8015610f8f5750828160060181518110610f7857fe5b6020910101516001600160f81b031916606d60f81b145b15610f9d5760019150610fa5565b600101610e50565b5080610fb85760001993505050506110b6565b60408051600481526024810182526020810180516001600160e01b031663797bf38560e01b178152915181516001600160a01b038916936113889392918291908083835b6020831061101b5780518252601f199092019160209182019101610ffc565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d806000811461107c576040519150601f19603f3d011682016040523d82523d6000602084013e611081565b606091505b509093509150826110995760001993505050506110b6565b8180602001905160208110156110ae57600080fd5b505193505050505b919050565b600f546001600160a01b031681565b601081565b602081565b6006546001600160a01b031681565b600d546001600160a01b031681565b61400081565b61080081565b600881565b6202000081565b6014546001600160a01b031681565b60606111288787878686610b55565b9150506111398787878785876114cb565b50505050505050565b6208000081565b600061115d826001600160a01b031661167c565b1561116b57506000196110b6565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b178152915181516000936060936001600160a01b0388169361138893919290918291908083835b602083106111d65780518252601f1990920191602091820191016111b7565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114611237576040519150601f19603f3d011682016040523d82523d6000602084013e61123c565b606091505b50915091508161125257600019925050506110b6565b6000805b60048351038110156113245782816000018151811061127157fe5b6020910101516001600160f81b031916604160f81b1480156112b4575082816001018151811061129d57fe5b6020910101516001600160f81b031916606160f81b145b80156112e157508281600201815181106112ca57fe5b6020910101516001600160f81b031916603b60f91b145b801561130e57508281600301815181106112f757fe5b6020910101516001600160f81b031916606560f81b145b1561131c5760019150611324565b600101611256565b50806113375760001993505050506110b6565b60408051600481526024810182526020810180516001600160e01b0316632274683f60e21b178152915181516001600160a01b038916936113889392918291908083836020831061101b5780518252601f199092019160209182019101610ffc565b61040081565b6003546001600160a01b031681565b600281565b6015546001600160a01b031681565b6007546001600160a01b031681565b6009546001600160a01b031681565b620cfdff81565b60003330146113f557600080fd5b6114008484846116b6565b90505b9392505050565b6017546001600160a01b031681565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b6012546001600160a01b031681565b6013546001600160a01b031681565b62080df081565b600181565b61020081565b601c546001600160a01b031681565b33301461148b57600080fd5b6114958282611c05565b5050565b6204000081565b600e546001600160a01b031681565b6201000081565b6008546001600160a01b031681565b61010081565b3330146114ed576114ed6001600160a01b03871633308763ffffffff611cba16565b6114fa8686868585611dd9565b60006115156001600160a01b0387163063ffffffff611e0c16565b9050838110156115565760405162461bcd60e51b815260040180806020018281038252603581526020018061653f6035913960400191505060405180910390fd5b333014611139576115776001600160a01b038716338363ffffffff611eb616565b506115ac336115956001600160a01b038a163063ffffffff611e0c16565b6001600160a01b038a16919063ffffffff611eb616565b5050505050505050565b6060826040519080825280602002602001820160405280156115e2578160200160208202803883390190505b50905060005b838110156116405761162087876116188761160c8a6001880163ffffffff611f3116565b9063ffffffff611f8a16565b600187610b55565b5082828151811061162d57fe5b60209081029190910101526001016115e8565b5095945050505050565b6000546001600160a01b031681565b600481565b6016546001600160a01b031681565b601e546001600160a01b031681565b60006001600160a01b03821615806116b057506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b92915050565b60006116ca846001600160a01b031661167c565b1561173957600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561171f57600080fd5b505af1158015611733573d6000803e3d6000fd5b50505050505b600a5460408051632ecd14d360e21b81526c42616e636f724e6574776f726b60981b600482015290516000926001600160a01b03169163bb34534c916024808301926020929190829003018186803b15801561179457600080fd5b505afa1580156117a8573d6000803e3d6000fd5b505050506040513d60208110156117be57600080fd5b5051600b549091506060906001600160a01b03908116906375e1cc82906117e690891661167c565b6117f057876117fd565b6007546001600160a01b03165b61180f886001600160a01b031661167c565b6118195787611826565b6007546001600160a01b03165b6040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b03168152602001826001600160a01b03166001600160a01b031681526020019250505060006040518083038186803b15801561188557600080fd5b505afa158015611899573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156118c257600080fd5b8101908080516040519392919084600160201b8211156118e157600080fd5b9083019060208201858111156118f657600080fd5b82518660208202830111600160201b8211171561191257600080fd5b82525081516020918201928201910280838360005b8381101561193f578181015183820152602001611927565b505050509050016040525050509050611980611963876001600160a01b031661167c565b61196d578661197a565b6007546001600160a01b03165b83611c05565b60006060836001600160a01b03166216e360856001600160a01b031663c7ba24bc905060e01b8589600160405160240180806020018481526020018360ff168152602001828103825285818151815260200191508051906020019060200280838360005b838110156119fc5781810151838201526020016119e4565b50505050905001945050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0383818316178352505050506040518082805190602001908083835b60208310611a6b5780518252601f199092019160209182019101611a4c565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038160008787f1925050503d8060008114611ace576040519150601f19603f3d011682016040523d82523d6000602084013e611ad3565b606091505b5091509150600082611ae6576000611afe565b818060200190516020811015611afb57600080fd5b50515b9050611b12886001600160a01b031661167c565b8015611b1e5750600081115b15611bf957600754604080516370a0823160e01b815230600482015290516001600160a01b0390921691632e1a7d4d9183916370a0823191602480820192602092909190829003018186803b158015611b7657600080fd5b505afa158015611b8a573d6000803e3d6000fd5b505050506040513d6020811015611ba057600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015611be057600080fd5b505af1158015611bf4573d6000803e3d6000fd5b505050505b98975050505050505050565b611c17826001600160a01b031661167c565b6114955760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b158015611c6c57600080fd5b505afa158015611c80573d6000803e3d6000fd5b505050506040513d6020811015611c9657600080fd5b5051901c611495576114956001600160a01b0383168260001963ffffffff611fcc16565b80611cc457611dd3565b611ccd8461167c565b15611db8576001600160a01b03831633148015611cea5750803410155b611d255760405162461bcd60e51b815260040180806020018281038252602b8152602001806165a3602b913960400191505060405180910390fd5b6001600160a01b0382163014611d6d576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015611d6b573d6000803e3d6000fd5b505b80341115611db357336108fc611d89348463ffffffff6120a216565b6040518115909202916000818181858888f19350505050158015611db1573d6000803e3d6000fd5b505b611dd3565b611dd36001600160a01b03851684848463ffffffff6120e416565b50505050565b836001600160a01b0316856001600160a01b03161415611df857611e05565b611e05858585858561213e565b5050505050565b6000611e178361167c565b15611e2d57506001600160a01b038116316116b0565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015611e8357600080fd5b505afa158015611e97573d6000803e3d6000fd5b505050506040513d6020811015611ead57600080fd5b50519392505050565b600081611ec557506001611403565b611ece8461167c565b15611f0f576040516001600160a01b0384169083156108fc029084906000818181858888f19350505050158015611f09573d6000803e3d6000fd5b50611403565b611f296001600160a01b038516848463ffffffff6124c716565b506001611403565b600082611f40575060006116b0565b82820282848281611f4d57fe5b04146114035760405162461bcd60e51b81526004018080602001828103825260218152602001806165ce6021913960400191505060405180910390fd5b600061140383836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612519565b611fd58361167c565b61209d57600081118015612063575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561203557600080fd5b505afa158015612049573d6000803e3d6000fd5b505050506040513d602081101561205f57600080fd5b5051115b15612083576120836001600160a01b03841683600063ffffffff6125bb16565b61209d6001600160a01b038416838363ffffffff6125bb16565b505050565b600061140383836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506126ce565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052611dd3908590612728565b836001600160a01b0316856001600160a01b0316141561215d57611e05565b61216f8161010063ffffffff6128e016565b6124ba57601854604080516302091f7b60e51b81526001600160a01b03888116600483015291516000939290921691634123ef6091602480820192602092909190829003018186803b1580156121c457600080fd5b505afa1580156121d8573d6000803e3d6000fd5b505050506040513d60208110156121ee57600080fd5b5051601854604080516302091f7b60e51b81526001600160a01b03898116600483015291519394506000939190921691634123ef60916024808301926020929190829003018186803b15801561224357600080fd5b505afa158015612257573d6000803e3d6000fd5b505050506040513d602081101561226d57600080fd5b5051905081801561227b5750805b1561248857606084516040519080825280602002602001820160405280156122ad578160200160208202803883390190505b50905060005b8551811015612301578581815181106122c857fe5b60200260200101516fffffffffffffffffffffffffffffffff168282815181106122ee57fe5b60209081029190910101526001016122b3565b50601a54604080516370a0823160e01b815230600482015290516000926001600160a01b0316916370a08231916024808301926020929190829003018186803b15801561234d57600080fd5b505afa158015612361573d6000803e3d6000fd5b505050506040513d602081101561237757600080fd5b5051601a54909150612397908a906001600160a01b0316898560006128e6565b60005b86518110156123da5760808782815181106123b157fe5b6020026020010151901c8382815181106123c757fe5b602090810291909101015260010161239a565b50601a54604080516370a0823160e01b815230600482015290516000926001600160a01b0316916370a08231916024808301926020929190829003018186803b15801561242657600080fd5b505afa15801561243a573d6000803e3d6000fd5b505050506040513d602081101561245057600080fd5b5051601a5490915061247e906001600160a01b03168a612476848663ffffffff6120a216565b866000612bad565b5050505050611e05565b81156124a35761249c8787878760006128e6565b5050611e05565b80156124b75761249c878787876000612bad565b50505b611e058585858585613395565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261209d908490612728565b600081836125a55760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561256a578181015183820152602001612552565b50505050905090810190601f1680156125975780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816125b157fe5b0495945050505050565b801580612641575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b15801561261357600080fd5b505afa158015612627573d6000803e3d6000fd5b505050506040513d602081101561263d57600080fd5b5051155b61267c5760405162461bcd60e51b81526004018080602001828103825260368152602001806166196036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261209d908490612728565b600081848411156127205760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561256a578181015183820152602001612552565b505050900390565b61273a826001600160a01b03166133a2565b61278b576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106127c95780518252601f1990920191602091820191016127aa565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461282b576040519150601f19603f3d011682016040523d82523d6000602084013e612830565b606091505b509150915081612887576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115611dd3578080602001905160208110156128a357600080fd5b5051611dd35760405162461bcd60e51b815260040180806020018281038252602a8152602001806165ef602a913960400191505060405180910390fd5b16151590565b6128ee6164b3565b6128f7866133de565b905080602001516001600160a01b031663415f1240856040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561294357600080fd5b505af1158015612957573d6000803e3d6000fd5b5050505060608351604051908082528060200260200182016040528015612988578160200160208202803883390190505b50905060005b8251518110156115ac57866001600160a01b0316836000015182815181106129b257fe5b6020026020010151600001516001600160a01b031614156129d257612ba5565b60005b8551811015612a1a57816008028682815181106129ee57fe5b6020026020010151901c60ff16838281518110612a0757fe5b60209081029190910101526001016129d5565b50306001600160a01b031663e2a7515e612a4e85600001518481518110612a3d57fe5b60200260200101516000015161362a565b8986600001518581518110612a5f57fe5b60209081029190910181015151604080516370a0823160e01b815230600482015290516001600160a01b03909216926370a0823192602480840193829003018186803b158015612aae57600080fd5b505afa158015612ac2573d6000803e3d6000fd5b505050506040513d6020811015612ad857600080fd5b50516040516001600160e01b031960e086901b1681526001600160a01b038085166004830190815290841660248301526044820183905260006064830181905260a483018c905260c0608484019081528a5160c48501528a5191938b938e9390929160e40190602086810191028083838a5b83811015612b62578181015183820152602001612b4a565b50505050905001975050505050505050600060405180830381600087803b158015612b8c57600080fd5b505af1158015612ba0573d6000803e3d6000fd5b505050505b60010161298e565b60608251604051908082528060200260200182016040528015612bda578160200160208202803883390190505b509050600019612be86164b3565b612bf1876133de565b90506000805b8251518110156131be576000612c39846040015161160c86600001518581518110612c1e57fe5b6020026020010151602001518c611f3190919063ffffffff16565b90508a6001600160a01b031684600001518381518110612c5557fe5b6020026020010151600001516001600160a01b031614612fcc57600084600001518381518110612c8157fe5b60209081029190910181015151604080516370a0823160e01b815230600482015290516001600160a01b03909216926370a0823192602480840193829003018186803b158015612cd057600080fd5b505afa158015612ce4573d6000803e3d6000fd5b505050506040513d6020811015612cfa57600080fd5b5051905060005b8951811015612d4657836008028a8281518110612d1a57fe5b6020026020010151901c60ff16888281518110612d3357fe5b6020908102919091010152600101612d01565b50612d668c612d5e87600001518681518110612a3d57fe5b848a8c613395565b600085600001518481518110612d7857fe5b60209081029190910181015151604080516370a0823160e01b815230600482015290516001600160a01b03909216926370a0823192602480840193829003018186803b158015612dc757600080fd5b505afa158015612ddb573d6000803e3d6000fd5b505050506040513d6020811015612df157600080fd5b81019080805190602001909291905050509050601960009054906101000a90046001600160a01b03166001600160a01b03166329a00e7c8d6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612e6157600080fd5b505afa158015612e75573d6000803e3d6000fd5b505050506040513d6020811015612e8b57600080fd5b50518851805188908110612e9b57fe5b6020026020010151600001516001600160a01b03166370a082318a602001516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015612f0057600080fd5b505afa158015612f14573d6000803e3d6000fd5b505050506040513d6020811015612f2a57600080fd5b505160408a0151612f41868863ffffffff6120a216565b6040518563ffffffff1660e01b8152600401808581526020018481526020018363ffffffff1663ffffffff16815260200182815260200194505050505060206040518083038186803b158015612f9657600080fd5b505afa158015612faa573d6000803e3d6000fd5b505050506040513d6020811015612fc057600080fd5b50519450613180915050565b601960009054906101000a90046001600160a01b03166001600160a01b03166329a00e7c8b6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561302957600080fd5b505afa15801561303d573d6000803e3d6000fd5b505050506040513d602081101561305357600080fd5b5051865180518690811061306357fe5b6020026020010151600001516001600160a01b03166370a0823188602001516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156130c857600080fd5b505afa1580156130dc573d6000803e3d6000fd5b505050506040513d60208110156130f257600080fd5b505160408089015181516001600160e01b031960e087901b1681526004810194909452602484019290925263ffffffff909116604483015260648201859052516084808301926020929190829003018186803b15801561315157600080fd5b505afa158015613165573d6000803e3d6000fd5b505050506040513d602081101561317b57600080fd5b505192505b8483101561318c578294505b6131b58460000151838151811061319f57fe5b6020026020010151600001518560200151611c05565b50600101612bf7565b5081602001516001600160a01b031663ca1d209d846040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561320957600080fd5b505af115801561321d573d6000803e3d6000fd5b506000925050505b82515181101561338957600061324484600001518381518110612a3d57fe5b905060008460000151838151811061325857fe5b60209081029190910181015151604080516370a0823160e01b815230600482015290516001600160a01b03909216926370a0823192602480840193829003018186803b1580156132a757600080fd5b505afa1580156132bb573d6000803e3d6000fd5b505050506040513d60208110156132d157600080fd5b5051604080516309be685f60e41b81526001600160a01b0385811660048301528e1660248201526044810183905290519192506000913091639be685f091606480830192602092919082900301818787803b15801561332f57600080fd5b505af1158015613343573d6000803e3d6000fd5b505050506040513d602081101561335957600080fd5b505190508061337e5761337c6001600160a01b038416338463ffffffff611eb616565b505b505050600101613225565b50505050505050505050565b611e058585858585613659565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906133d657508115155b949350505050565b6133e66164b3565b6000826001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561342157600080fd5b505afa158015613435573d6000803e3d6000fd5b505050506040513d602081101561344b57600080fd5b50516001600160a01b0381166020848101829052604080516371f52bf360e01b8152905193945091926371f52bf3926004808201939291829003018186803b15801561349657600080fd5b505afa1580156134aa573d6000803e3d6000fd5b505050506040513d60208110156134c057600080fd5b50516040805182815260208084028201019091529080156134fb57816020015b6134e86164dd565b8152602001906001900390816134e05790505b50825260005b82515181101561362357816001600160a01b03166319b64015826040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561354f57600080fd5b505afa158015613563573d6000803e3d6000fd5b505050506040513d602081101561357957600080fd5b5051835180518390811061358957fe5b60209081029190910101516001600160a01b039091169052825180516135c6918491849081106135b557fe5b602002602001015160000151613938565b83518051839081106135d457fe5b60200260200101516020018181525050613616836000015182815181106135f757fe5b6020026020010151602001518460400151613ae190919063ffffffff16565b6040840152600101613501565b5050919050565b601d546000906001600160a01b0383811691161461364857816116b0565b5050601c546001600160a01b031690565b836001600160a01b0316856001600160a01b0316141561367857611e05565b61368b816208000063ffffffff6128e016565b61392b576006546001600160a01b03868116911614156137a057600654604080516370a0823160e01b815230600482015290516001600160a01b0390921691632e1a7d4d9183916370a0823191602480820192602092909190829003018186803b1580156136f857600080fd5b505afa15801561370c573d6000803e3d6000fd5b505050506040513d602081101561372257600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561376257600080fd5b505af1158015613776573d6000803e3d6000fd5b5050505061379b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee85858585613b3b565b611e05565b6007546001600160a01b038681169116141561380957600754604080516370a0823160e01b815230600482015290516001600160a01b0390921691632e1a7d4d9183916370a0823191602480820192602092909190829003018186803b1580156136f857600080fd5b6006546001600160a01b03858116911614156138a4576138408573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585613659565b600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b15801561389057600080fd5b505af115801561247e573d6000803e3d6000fd5b6007546001600160a01b038581169116141561392b576138db8573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585613659565b600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b15801561389057600080fd5b611e058585858585613b3b565b604080516001600160a01b0383811660248084019190915283518084039091018152604490920183526020820180516001600160e01b03166316095a2360e11b17815292518251600094859460609489169361271093919290918291908083835b602083106139b85780518252601f199092019160209182019101613999565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613a19576040519150601f19603f3d011682016040523d82523d6000602084013e613a1e565b606091505b50915091508115613a4a57808060200190516020811015613a3e57600080fd5b505192506116b0915050565b6000856001600160a01b0316630e53aae9866040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060a06040518083038186803b158015613aa257600080fd5b505afa158015613ab6573d6000803e3d6000fd5b505050506040513d60a0811015613acc57600080fd5b506020015163ffffffff169695505050505050565b600082820183811015611403576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b611e0585858585855b836001600160a01b0316856001600160a01b03161415613b6357611e05565b613b6b6164f4565b613b73613f3b565b9050613b878261080063ffffffff6128e016565b613f265760005b600a811015613d46578181600a8110613ba357fe5b60200201516001600160a01b0316876001600160a01b03161415613d3e5760008282600a8110613bcf57fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015613c0c57600080fd5b505afa158015613c20573d6000803e3d6000fd5b505050506040513d6020811015613c3657600080fd5b505190508282600a8110613c4657fe5b60200201516001600160a01b0316632e1a7d4d876040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015613c9057600080fd5b505af1158015613ca4573d6000803e3d6000fd5b50505050613d368188836001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015613d0357600080fd5b505afa158015613d17573d6000803e3d6000fd5b505050506040513d6020811015613d2d57600080fd5b50518888613b44565b505050611e05565b600101613b8e565b5060005b600a811015613f24578181600a8110613d5f57fe5b60200201516001600160a01b0316866001600160a01b03161415613f1c5760008282600a8110613d8b57fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015613dc857600080fd5b505afa158015613ddc573d6000803e3d6000fd5b505050506040513d6020811015613df257600080fd5b50519050613e038882888888614057565b613e1d818484600a8110613e1357fe5b6020020151611c05565b8282600a8110613e2957fe5b60200201516001600160a01b031663b6b55f25826001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015613e9257600080fd5b505afa158015613ea6573d6000803e3d6000fd5b505050506040513d6020811015613ebc57600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015613efc57600080fd5b505af1158015613f10573d6000803e3d6000fd5b50505050505050611e05565b600101613d4a565b505b613f338686868686614057565b505050505050565b613f436164f4565b5060408051610140810182527316de59092dae5ccf4a1e6439d611fd0653f0bd0181527304aa51bbcb46541455ccf1b8bef2ebc5d3787ec960208201527373a052500105205d34daf004eab301916da8190f918101919091527383f798e925bcd4017eb265844fddabb448f1707d606082015273d6ad7a6750a7593e092a9b218d66c0a814a3436e608082015273f61718057901f84c4eec4339ef8f0d86d2b4560060a08201527304bc0ab673d88ae9dbc9da2380cb6b79c4bca9ae60c082015273c2cb1040220768554cf699b0d863a3cd4324ce3260e082015273e6354ed5bc4b393a5aad09f21c46e101e692d4476101008201527326ea744e5b887e5205727f55dfbe8685e3b2195161012082015290565b611e0585858585855b836001600160a01b0316856001600160a01b0316141561407f57611e05565b61409081601063ffffffff6128e016565b6142995761409d856142a6565b1561414c5760006140ad866143d9565b9050856001600160a01b031663db006a75856040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156140f557600080fd5b505af1158015614109573d6000803e3d6000fd5b505050506040513d602081101561411f57600080fd5b506000905061413d6001600160a01b0383163063ffffffff611e0c16565b905061249c8287838787614060565b614155846142a6565b15614299576000614165856143d9565b90506141748682868686614500565b600061418f6001600160a01b0383163063ffffffff611e0c16565b90506141a3826001600160a01b031661167c565b1561421657601760009054906101000a90046001600160a01b03166001600160a01b0316631249c58b826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156141f857600080fd5b505af115801561420c573d6000803e3d6000fd5b505050505061249c565b6142208287611c05565b856001600160a01b031663a0712d68826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561426657600080fd5b505af115801561427a573d6000803e3d6000fd5b505050506040513d602081101561429057600080fd5b50505050611e05565b611e058585858585614500565b6017546000906001600160a01b03838116911614156142c7575060016110b6565b601654604080516001600160a01b0385811660248084019190915283518084039091018152604490920183526020820180516001600160e01b0316638e8f294b60e01b17815292518251600095606095931693611388939092918291908083835b602083106143475780518252601f199092019160209182019101614328565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d80600081146143a8576040519150601f19603f3d011682016040523d82523d6000602084013e6143ad565b606091505b5091509150816143c2576000925050506110b6565b60008180602001905160408110156110ae57600080fd5b6017546000906001600160a01b03838116911614156143fa575060006110b6565b60408051600481526024810182526020810180516001600160e01b0316636f307dc360e01b178152915181516000936060936001600160a01b0388169361138893919290918291908083835b602083106144655780518252601f199092019160209182019101614446565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d80600081146144c6576040519150601f19603f3d011682016040523d82523d6000602084013e6144cb565b606091505b5091509150816144e157600019925050506110b6565b8080602001905160208110156144f657600080fd5b5051949350505050565b611e058585858585836001600160a01b0316856001600160a01b0316141561452757611e05565b61453881602063ffffffff6128e016565b6147cb57600061454786610d43565b90506001600160a01b03808216146146975761456b816001600160a01b031661167c565b156145f1576040805163081a6b2560e41b81523060048201526024810186905290516001600160a01b038816916381a6b2509160448083019260209291908290030181600087803b1580156145bf57600080fd5b505af11580156145d3573d6000803e3d6000fd5b505050506040513d60208110156145e957600080fd5b5061466d9050565b60408051632770a7eb60e21b81523060048201526024810186905290516001600160a01b03881691639dc29fac9160448083019260209291908290030181600087803b15801561464057600080fd5b505af1158015614654573d6000803e3d6000fd5b505050506040513d602081101561466a57600080fd5b50505b60006146886001600160a01b0383163063ffffffff611e0c16565b905061249c82878387876147d4565b6146a085610d43565b90506001600160a01b03808216146147c9576146bf86828686866147d4565b60006146da6001600160a01b0383163063ffffffff611e0c16565b90506146ee826001600160a01b031661167c565b156147705760408051638f6ede1f60e01b815230600482015290516001600160a01b03881691638f6ede1f91849160248082019260209290919082900301818588803b15801561473d57600080fd5b505af1158015614751573d6000803e3d6000fd5b50505050506040513d602081101561476857600080fd5b5061249c9050565b61477a8287611c05565b604080516340c10f1960e01b81523060048201526024810183905290516001600160a01b038816916340c10f199160448083019260209291908290030181600087803b15801561426657600080fd5b505b611e0585858585855b611e0585858585855b836001600160a01b0316856001600160a01b031614156147fc57611e05565b61480d81608063ffffffff6128e016565b614a1d57600061481c86611149565b90506001600160a01b038082161461489f57856001600160a01b031663db006a75856040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561487457600080fd5b505af1158015614888573d6000803e3d6000fd5b5050505061489981868686866147dd565b50611e05565b6148a885611149565b90506001600160a01b0380821614614a1b576148c78682868686614a26565b60006148e26001600160a01b0383163063ffffffff611e0c16565b905061496782601560009054906101000a90046001600160a01b03166001600160a01b031663f2f4eb266040518163ffffffff1660e01b815260040160206040518083038186803b15801561493657600080fd5b505afa15801561494a573d6000803e3d6000fd5b505050506040513d602081101561496057600080fd5b5051611c05565b6015546001600160a01b039081169063d2d0e0669061498790851661167c565b614992576000614994565b825b6149a6856001600160a01b031661167c565b6149b057846149c6565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e086901b1681526001600160a01b0390921660048301526024820186905261044d604483015251606480830192600092919082900301818588803b158015613efc57600080fd5b505b611e0585858585855b836001600160a01b0316856001600160a01b03161415614a4557611e05565b614a578161040063ffffffff6128e016565b614ccb576013546001600160a01b0386811691161415614bac5760135460408051637f8661a160e01b81526004810186905290516001600160a01b0390921691637f8661a19160248082019260009290919082900301818387803b158015614abe57600080fd5b505af1158015614ad2573d6000803e3d6000fd5b5050601454604080516370a0823160e01b81523060048201529051600094506001600160a01b0390921692506370a08231916024808301926020929190829003018186803b158015614b2357600080fd5b505afa158015614b37573d6000803e3d6000fd5b505050506040513d6020811015614b4d57600080fd5b505190508015614b9357601454606090614b74906001600160a01b03168784600187610b55565b601454909250614b9191506001600160a01b031687848487611dd9565b505b600054614899906001600160a01b031686868686614cd4565b6013546001600160a01b0385811691161415614ccb57600054614bdc9086906001600160a01b0316858585614cd4565b600054601354614bf8916001600160a01b039081169116611c05565b601354600054604080516370a0823160e01b815230600482015290516001600160a01b039384169363049878f39316916370a08231916024808301926020929190829003018186803b158015614c4d57600080fd5b505afa158015614c61573d6000803e3d6000fd5b505050506040513d6020811015614c7757600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015614cb757600080fd5b505af1158015614290573d6000803e3d6000fd5b611e0585858585855b836001600160a01b0316856001600160a01b03161415614cf357611e05565b614d0481604063ffffffff6128e016565b614f2b576012546001600160a01b0386811691161415614e0e576012546040805163ef693bed60e01b81523060048201526024810186905290516001600160a01b039092169163ef693bed9160448082019260009290919082900301818387803b158015614d7157600080fd5b505af1158015614d85573d6000803e3d6000fd5b5050600054604080516370a0823160e01b8152306004820152905161379b94506001600160a01b039092169250879183916370a08231916024808301926020929190829003018186803b158015614ddb57600080fd5b505afa158015614def573d6000803e3d6000fd5b505050506040513d6020811015614e0557600080fd5b50518585614f34565b6012546001600160a01b0385811691161415614f2b57600054614e3e9086906001600160a01b0316858585614f34565b600054601254614e5a916001600160a01b039081169116611c05565b601254600054604080516370a0823160e01b8152306004820181905291516001600160a01b0394851694633b4da69f9416916370a08231916024808301926020929190829003018186803b158015614eb157600080fd5b505afa158015614ec5573d6000803e3d6000fd5b505050506040513d6020811015614edb57600080fd5b5051604080516001600160e01b031960e086901b1681526001600160a01b039093166004840152602483019190915251604480830192600092919082900301818387803b158015614cb757600080fd5b611e0585858585855b614f46856001600160a01b031661167c565b158015614f625750614f60846001600160a01b031661167c565b155b8015614f7b5750614f7b8161020063ffffffff6128e016565b1561507c5760608251604051908082528060200260200182016040528015614fad578160200160208202803883390190505b50905060005b8351811015614ff257838181518110614fc857fe5b602002602001015160ff16828281518110614fdf57fe5b6020908102919091010152600101614fb3565b506150148673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee868486615396565b60005b835181101561505a57600884828151811061502e57fe5b6020026020010151901c60ff1682828151811061504757fe5b6020908102919091010152600101615017565b5061489973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee86478486615396565b6000546001600160a01b038681169116148015906150a857506000546001600160a01b03858116911614155b80156150c257506150c2816201000063ffffffff6128e016565b1561521e57606082516040519080825280602002602001820160405280156150f4578160200160208202803883390190505b50905060005b83518110156151395783818151811061510f57fe5b602002602001015160ff1682828151811061512657fe5b60209081029190910101526001016150fa565b506000546151549087906001600160a01b0316868486615396565b60005b835181101561519a57600884828151811061516e57fe5b6020026020010151901c60ff1682828151811061518757fe5b6020908102919091010152600101615157565b50600054604080516370a0823160e01b81523060048201529051614899926001600160a01b031691889183916370a08231916024808301926020929190829003018186803b1580156151eb57600080fd5b505afa1580156151ff573d6000803e3d6000fd5b505050506040513d602081101561521557600080fd5b50518486615396565b6001546001600160a01b0386811691161480159061524a57506001546001600160a01b03858116911614155b80156152645750615264816202000063ffffffff6128e016565b1561538d5760608251604051908082528060200260200182016040528015615296578160200160208202803883390190505b50905060005b83518110156152db578381815181106152b157fe5b602002602001015160ff168282815181106152c857fe5b602090810291909101015260010161529c565b506001546152f69087906001600160a01b0316868486615396565b60005b835181101561533c57600884828151811061531057fe5b6020026020010151901c60ff1682828151811061532957fe5b60209081029190910101526001016152f9565b50600154604080516370a0823160e01b81523060048201529051614899926001600160a01b031691889183916370a08231916024808301926020929190829003018186803b1580156151eb57600080fd5b611e0585858585855b836001600160a01b0316856001600160a01b031614156153b557611e05565b6153bd616513565b50604080516101208101825261554581526157a96020820152615918918101919091526159356060820152615bda6080820152615d3a60a0820152615ebc60c082015261608d60e0820152616262610100820152600080805b600981101561546f57600086828151811061542d57fe5b602002602001015111156154675761546186828151811061544a57fe5b602002602001015184613ae190919063ffffffff16565b92508091505b600101615416565b50600082116154af5760405162461bcd60e51b815260040180806020018281038252602f815260200180616574602f913960400191505060405180910390fd5b8560005b6009811015613389578681815181106154c857fe5b6020026020010151600014156154dd5761553d565b60006155098561160c8a85815181106154f257fe5b60200260200101518c611f3190919063ffffffff16565b9050838214156155165750815b808303925061553a8b8b8389866009811061552d57fe5b602002015163ffffffff16565b50505b6001016154b3565b60008161555a6001600160a01b03861661167c565b61567d57600954604080516303795fb160e11b81526001600160a01b038881166004830152915160009392909216916306f2bf6291602480820192602092909190829003018186803b1580156155af57600080fd5b505afa1580156155c3573d6000803e3d6000fd5b505050506040513d60208110156155d957600080fd5b505190506001600160a01b0381161561567b576155f68682611c05565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561564c57600080fd5b505af1158015615660573d6000803e3d6000fd5b505050506040513d602081101561567657600080fd5b505191505b505b61568f846001600160a01b031661167c565b61140057600954604080516303795fb160e11b81526001600160a01b038781166004830152915160009392909216916306f2bf6291602480820192602092909190829003018186803b1580156156e457600080fd5b505afa1580156156f8573d6000803e3d6000fd5b505050506040513d602081101561570e57600080fd5b505190506001600160a01b038116156157a057806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b15801561577057600080fd5b505af1158015615784573d6000803e3d6000fd5b50505050506040513d602081101561579b57600080fd5b505191505b50949350505050565b6008546000906157c39085906001600160a01b0316611c05565b6008546001600160a01b03908116906329589f61906157e390871661167c565b6157ee5760006157f0565b835b615802876001600160a01b031661167c565b61580c5786615822565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b85615835886001600160a01b031661167c565b61583f5787615855565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b1580156158ed57600080fd5b505af1158015615901573d6000803e3d6000fd5b50505050506040513d60208110156144f657600080fd5b6000806159268585856116b6565b90506000811161140057600080fd5b6000615949846001600160a01b031661167c565b156159b857600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561599e57600080fd5b505af11580156159b2573d6000803e3d6000fd5b50505050505b6159f56159cd856001600160a01b031661167c565b6159d757846159e4565b6006546001600160a01b03165b600c546001600160a01b0316611c05565b600c546000906001600160a01b0390811690630621b4f690615a1890881661167c565b615a225786615a2f565b6006546001600160a01b03165b85615a42886001600160a01b031661167c565b615a4c5787615a59565b6006546001600160a01b03165b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b158015615ab757600080fd5b505af1158015615acb573d6000803e3d6000fd5b505050506040513d6020811015615ae157600080fd5b50519050615af76001600160a01b03851661167c565b1561140057600654604080516370a0823160e01b815230600482015290516001600160a01b0390921691632e1a7d4d9183916370a0823191602480820192602092909190829003018186803b158015615b4f57600080fd5b505afa158015615b63573d6000803e3d6000fd5b505050506040513d6020811015615b7957600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015615bb957600080fd5b505af1158015615bcd573d6000803e3d6000fd5b5050505090509392505050565b60015460009081906001600160a01b03868116911614615bfb576000615bfe565b60025b6000546001600160a01b03878116911614615c1a576000615c1d565b60015b600154910160ff1691506000906001600160a01b0390811690861614615c44576000615c47565b60025b6000546001600160a01b03878116911614615c63576000615c66565b60015b0160ff16905081600f0b60001480615c81575080600f0b6000145b15615c9157600092505050611403565b600d54615ca89087906001600160a01b0316611c05565b600d5460408051635320bf6b60e11b8152600019808601600f90810b810b6004840152908501810b900b60248201526044810187905260006064820181905291516001600160a01b039093169263a6417ed69260848084019391929182900301818387803b158015615d1957600080fd5b505af1158015615d2d573d6000803e3d6000fd5b5050505050509392505050565b60025460009081906001600160a01b03868116911614615d5b576000615d5e565b60035b6001546001600160a01b03878116911614615d7a576000615d7d565b60025b6000546001600160a01b03888116911614615d99576000615d9c565b60015b60025491019190910160ff1691506000906001600160a01b0390811690861614615dc7576000615dca565b60035b6001546001600160a01b03878116911614615de6576000615de9565b60025b6000546001600160a01b03888116911614615e05576000615e08565b60015b010160ff16905081600f0b60001480615e24575080600f0b6000145b15615e3457600092505050611403565b600e54615e4b9087906001600160a01b0316611c05565b600e5460408051635320bf6b60e11b8152600019808601600f90810b810b6004840152908501810b900b60248201526044810187905260006064820181905291516001600160a01b039093169263a6417ed69260848084019391929182900301818387803b158015615d1957600080fd5b60035460009081906001600160a01b03868116911614615edd576000615ee0565b60045b6002546001600160a01b03878116911614615efc576000615eff565b60035b6001546001600160a01b03888116911614615f1b576000615f1e565b60025b6000546001600160a01b03898116911614615f3a576000615f3d565b60015b01010160ff1690506000600360009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b031614615f7c576000615f7f565b60045b6002546001600160a01b03878116911614615f9b576000615f9e565b60035b6001546001600160a01b03888116911614615fba576000615fbd565b60025b6000546001600160a01b03898116911614615fd9576000615fdc565b60015b01010160ff16905081600f0b60001480615ff9575080600f0b6000145b1561600957600092505050611403565b600f546160209087906001600160a01b0316611c05565b600f805460408051635320bf6b60e11b8152600019808701850b850b60048301528501840b90930b60248401526044830187905260006064840181905290516001600160a01b039092169263a6417ed6926084808301939282900301818387803b158015615d1957600080fd5b60045460009081906001600160a01b038681169116146160ae5760006160b1565b60045b6002546001600160a01b038781169116146160cd5760006160d0565b60035b6001546001600160a01b038881169116146160ec5760006160ef565b60025b6000546001600160a01b0389811691161461610b57600061610e565b60015b01010160ff1690506000600460009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b03161461614d576000616150565b60045b6002546001600160a01b0387811691161461616c57600061616f565b60035b6001546001600160a01b0388811691161461618b57600061618e565b60025b6000546001600160a01b038981169116146161aa5760006161ad565b60015b01010160ff16905081600f0b600014806161ca575080600f0b6000145b156161da57600092505050611403565b6010546161f19087906001600160a01b0316611c05565b60105460408051635320bf6b60e11b8152600019808601600f90810b810b6004840152908501810b900b60248201526044810187905260006064820181905291516001600160a01b039093169263a6417ed69260848084019391929182900301818387803b158015615d1957600080fd5b60055460009081906001600160a01b03868116911614616283576000616286565b60055b6003546001600160a01b038781169116146162a25760006162a5565b60045b6002546001600160a01b038881169116146162c15760006162c4565b60035b6001546001600160a01b038981169116146162e05760006162e3565b60025b6000546001600160a01b038a81169116146162ff576000616302565b60015b0101010160ff1690506000600560009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b031614616342576000616345565b60055b6003546001600160a01b03878116911614616361576000616364565b60045b6002546001600160a01b03888116911614616380576000616383565b60035b6001546001600160a01b0389811691161461639f5760006163a2565b60025b6000546001600160a01b038a81169116146163be5760006163c1565b60015b0101010160ff16905081600f0b600014806163df575080600f0b6000145b156163ef57600092505050611403565b6005546001600160a01b0387811691161480159061641b57506005546001600160a01b03868116911614155b1561642b57600092505050611403565b6011546164429087906001600160a01b0316611c05565b60115460408051635320bf6b60e11b8152600019808601600f90810b810b6004840152908501810b900b60248201526044810187905260006064820181905291516001600160a01b039093169263a6417ed69260848084019391929182900301818387803b158015615d1957600080fd5b60405180606001604052806060815260200160006001600160a01b03168152602001600081525090565b604080518082019091526000808252602082015290565b604051806101400160405280600a906020820280388339509192915050565b6040518061012001604052806009905b61653c8152602001906001900390816165235790505090565bfefe4f6e6553706c69743a2061637475616c2072657475726e20616d6f756e74206973206c657373207468616e206d696e52657475726e4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f7357726f6e6720757365616765206f66204554482e756e6976657273616c5472616e7366657246726f6d2829536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a265627a7a72315820181695d831e5f656e86618f59a1f16e8b49edf4672a4198ae0257b8702135db164736f6c63430005100032 \ No newline at end of file diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index eef30fe..caf1097 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -474,14 +474,16 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { for (uint i = 0; i < details.tokens.length; i++) { IERC20 reserveToken = _canonicalSUSD(details.tokens[i].token); - uint256 ret = _swapOnBancorSafe( + uint256 leftover = details.tokens[i].token.balanceOf(address(this)); + + uint256 ret = this.swapOnBancorSafe( reserveToken, smartToken, - details.tokens[i].token.balanceOf(address(this)) + leftover ); if (ret == 0) { - reserveToken.universalTransfer(msg.sender, amount); + reserveToken.universalTransfer(msg.sender, leftover); } } } From 014a5af2cbe0c6866dfc07d2dbd97052d2be034a Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Mon, 6 Apr 2020 00:05:19 +0300 Subject: [PATCH 29/60] Revert abi and bin --- OneSplit.full.abi | 2 +- OneSplit.full.bin | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OneSplit.full.abi b/OneSplit.full.abi index adf9c53..94b8183 100644 --- a/OneSplit.full.abi +++ b/OneSplit.full.abi @@ -1 +1 @@ -[{"inputs":[{"internalType":"contract IOneSplitView","name":"_oneSplitView","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[],"name":"ETH_ADDRESS","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_ALL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_ALL_SOURCES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_ALL_WRAPPERS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BANCOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BDAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_BINANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_SYNTHETIX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_USDT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_Y","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_FULCRUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IEARN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_KYBER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_OASIS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_SMART_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_WETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_BANCOR_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_OASIS_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_UNISWAP_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_DAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_USDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"_isAaveToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"_isFulcrumToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"aave","outputs":[{"internalType":"contract IAaveLendingPool","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"acientSUSD","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorContractRegistry","outputs":[{"internalType":"contract IBancorContractRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorEtherToken","outputs":[{"internalType":"contract IBancorEtherToken","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bdai","outputs":[{"internalType":"contract IBdai","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"btu","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"busd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cETH","outputs":[{"internalType":"contract ICompoundEther","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"chai","outputs":[{"internalType":"contract IChai","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"compound","outputs":[{"internalType":"contract ICompound","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveBinance","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveCompound","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveSynthetix","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveUsdt","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveY","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"getAllRatesForDEX","outputs":[{"internalType":"uint256[]","name":"results","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"getExpectedReturn","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"goodSwap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"infiniteApproveIfNeeded","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"kyberNetworkProxy","outputs":[{"internalType":"contract IKyberNetworkProxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oasisExchange","outputs":[{"internalType":"contract IOasisExchange","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oneSplitView","outputs":[{"internalType":"contract IOneSplitView","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"susd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"swap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"swapOnBancorSafe","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tusd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"uniswapFactory","outputs":[{"internalType":"contract IUniswapFactory","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdc","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"wethToken","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file +[{"inputs":[{"internalType":"contract IOneSplitView","name":"_oneSplitView","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[],"name":"ETH_ADDRESS","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BANCOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BDAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_BINANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_SYNTHETIX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_USDT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_Y","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_FULCRUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IEARN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_KYBER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_OASIS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_SMART_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_WETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_BANCOR_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_OASIS_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_UNISWAP_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_DAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_USDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"_isAaveToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"_isFulcrumToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"aave","outputs":[{"internalType":"contract IAaveLendingPool","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorContractRegistry","outputs":[{"internalType":"contract IBancorContractRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorEtherToken","outputs":[{"internalType":"contract IBancorEtherToken","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bdai","outputs":[{"internalType":"contract IBdai","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"btu","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"busd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cETH","outputs":[{"internalType":"contract ICompoundEther","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"chai","outputs":[{"internalType":"contract IChai","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"compound","outputs":[{"internalType":"contract ICompound","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveBinance","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveCompound","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveSynthetix","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveUsdt","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveY","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"getAllRatesForDEX","outputs":[{"internalType":"uint256[]","name":"results","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"getExpectedReturn","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"goodSwap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"kyberNetworkProxy","outputs":[{"internalType":"contract IKyberNetworkProxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oasisExchange","outputs":[{"internalType":"contract IOasisExchange","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oneSplitView","outputs":[{"internalType":"contract IOneSplitView","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"susd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minReturn","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"},{"internalType":"uint256","name":"disableFlags","type":"uint256"}],"name":"swap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"tusd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"uniswapFactory","outputs":[{"internalType":"contract IUniswapFactory","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdc","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"wethToken","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/OneSplit.full.bin b/OneSplit.full.bin index 43612e9..d89f14b 100644 --- a/OneSplit.full.bin +++ b/OneSplit.full.bin @@ -1 +1 @@ -6080604052600080546001600160a01b0319908116736b175474e89094c44da98b954eedeac495271d0f1790915560018054821673a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4817905560028054821673dac17f958d2ee523a2206206994597c13d831ec71790556003805482166e085d4780b73119b644ae5ecd22b376179055600480548216734fabb145d64652a948d72533023f6e7a623c7c531790556005805482167357ab1ec28d129707052df4df418d58a2d46d5f5190811790915560068054831673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc217905560078054831673c0829421c1d260bd3cb3e0f06cfe2d52db2ce31517905560088054831673818e6fecd516ecc3849daf6845e3ec868087b75517905560098054831673c0a47dfe034b400b47bdad5fecda2621de6c4d95179055600a805483167352ae12abe5d8bd778bd5397f99ca900624cfadd4179055600b80548316736f0cd8c4f6f06eab664c7e3031909452b4b72861179055600c8054831673794e6e91555438afc3ccf1c5076a74f42133d08d179055600d8054831673a2b47e3d5c44877cca798226b7b8118f9bfb7a56179055600e805483167352ea46506b9cc5ef470c5bf89f17dc28bb35d85c179055600f805483167345f783cce6b7ff23b2ab2d70e416cdb7d6055f511790556010805483167379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27179055601180548316733b12e1fbb468bea80b492d635976809bf950186c1790556012805483167306af07097c9eeb7fd685c692751d5c66db49c215179055601380548316736a4ffaafa8dd400676df8076ad6c724867b0e2e817905560148054831673b683d83a532e2cb7dfa5275eed3698436371cc9f17905560158054831673398ec7346dcd622edc5ae82352f02be94c62d119179055601680548316733d9819210a31b4961b30ef54be2aed79b9c9cd3b179055601780548316734ddc2d193948926d02f9b1fe9e1daa0718270ed517905560188054831673f6e2d7f616b67e46d708e4410746e9aab3a4c51817905560198054831673524619eb9b4cdffa7da13029b33f24635478afc0179055601a80548316731f573d6fb3f13d689ff844b4ce37794d79a7ff1c179055601b8054831673309627af60f0926daa6041b8279484312f2bf060179055601c805483169091179055601d80549091167357ab1e02fee23774580c119740129eac7081e9d31790553480156200038c57600080fd5b5060405162006a6638038062006a6683398181016040526020811015620003b257600080fd5b5051601e80546001600160a01b0319166001600160a01b0390921691909117905561668380620003e36000396000f3fe6080604052600436106103975760003560e01c806375a8b012116101dc578063c762a46c11610102578063d77366a4116100a0578063f4b9fa751161006f578063f4b9fa7514610b01578063f56e281f14610b16578063f69e204614610b2b578063fbe4ed9514610b4057610397565b8063d77366a414610970578063dc1536b214610985578063e2a7515e1461099a578063f484966b14610a6257610397565b8063c9b27359116100dc578063c9b27359146108f6578063c9b42c6714610931578063cede5f6a14610946578063d393c3e91461095b57610397565b8063c762a46c146108b7578063c77b9de6146108cc578063c9257775146108e157610397565b80639be685f01161017a578063b3bc784411610149578063b3bc784414610863578063b69d045614610878578063c11f4f111461088d578063c2ea9fb0146108a257610397565b80639be685f0146107e1578063a1b4d01114610824578063a734f06e14610839578063b0a7ef291461084e57610397565b8063819faf7b116101b6578063819faf7b1461078d578063851954fa146107a25780638bdb2afa146107b757806390fd1c9b146107cc57610397565b806375a8b0121461074e57806375b5be2d146107635780637a88bdbd1461077857610397565b80633e413bee116102c15780635aa8fb481161025f5780636b5a4ca21161022e5780636b5a4ca2146106a95780636b9589aa146106be5780636cbc4a6e1461070657806372b6f1bf1461071b57610397565b80635aa8fb48146106555780635ae51b821461066a5780635c0cb4791461067f57806364ec4e5c1461069457610397565b806344211d621161029b57806344211d62146106015780634a7101d5146106165780634b57b0be1461062b57806351f1985c1461064057610397565b80633e413bee146105a45780634037f967146105b9578063423d03f9146105ec57610397565b806323c2257c116103395780632f48ab7d116103085780632f48ab7d1461055057806334b4dabb14610565578063372a26cb1461057a5780633ca5b2341461058f57610397565b806323c2257c146104fc57806325636385146105115780632d3b5207146105265780632e707bd21461053b57610397565b8063139891401161037557806313989140146104965780632113240d146104bd57806321a360f5146104d257806322320c98146104e757610397565b8063085e2c5b146103a657806312dea160146104505780631388b42014610481575b333214156103a457600080fd5b005b3480156103b257600080fd5b506103f5600480360360a08110156103c957600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060800135610b55565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b8381101561043b578181015183820152602001610423565b50505050905001935050505060405180910390f35b34801561045c57600080fd5b50610465610c9d565b604080516001600160a01b039092168252519081900360200190f35b34801561048d57600080fd5b50610465610cac565b3480156104a257600080fd5b506104ab610cbb565b60408051918252519081900360200190f35b3480156104c957600080fd5b506104ab610cc1565b3480156104de57600080fd5b506104ab610cc7565b3480156104f357600080fd5b50610465610cd0565b34801561050857600080fd5b50610465610cdf565b34801561051d57600080fd5b506104ab610cee565b34801561053257600080fd5b506104ab610cf5565b34801561054757600080fd5b506104ab610cfd565b34801561055c57600080fd5b50610465610d02565b34801561057157600080fd5b506104ab610d11565b34801561058657600080fd5b50610465610d16565b34801561059b57600080fd5b50610465610d25565b3480156105b057600080fd5b50610465610d34565b3480156105c557600080fd5b50610465600480360360208110156105dc57600080fd5b50356001600160a01b0316610d43565b3480156105f857600080fd5b506104656110bb565b34801561060d57600080fd5b506104ab6110ca565b34801561062257600080fd5b506104ab6110cf565b34801561063757600080fd5b506104656110d4565b34801561064c57600080fd5b506104656110e3565b34801561066157600080fd5b506104ab6110f2565b34801561067657600080fd5b506104ab6110f8565b34801561068b57600080fd5b506104ab6110fe565b3480156106a057600080fd5b506104ab611103565b3480156106b557600080fd5b5061046561110a565b6103a4600480360360c08110156106d457600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060808101359060a00135611119565b34801561071257600080fd5b506104ab611142565b34801561072757600080fd5b506104656004803603602081101561073e57600080fd5b50356001600160a01b0316611149565b34801561075a57600080fd5b506104ab611399565b34801561076f57600080fd5b5061046561139f565b34801561078457600080fd5b506104ab6113ae565b34801561079957600080fd5b506104656113b3565b3480156107ae57600080fd5b506104656113c2565b3480156107c357600080fd5b506104656113d1565b3480156107d857600080fd5b506104ab6113e0565b3480156107ed57600080fd5b506104ab6004803603606081101561080457600080fd5b506001600160a01b038135811691602081013590911690604001356113e7565b34801561083057600080fd5b5061046561140a565b34801561084557600080fd5b50610465611419565b34801561085a57600080fd5b506104ab611431565b34801561086f57600080fd5b506104ab611437565b34801561088457600080fd5b50610465611440565b34801561089957600080fd5b5061046561144f565b3480156108ae57600080fd5b506104ab61145e565b3480156108c357600080fd5b506104ab611465565b3480156108d857600080fd5b506104ab61146a565b3480156108ed57600080fd5b50610465611470565b34801561090257600080fd5b506103a46004803603604081101561091957600080fd5b506001600160a01b038135811691602001351661147f565b34801561093d57600080fd5b506104ab611499565b34801561095257600080fd5b506104656114a0565b34801561096757600080fd5b506104ab6114af565b34801561097c57600080fd5b506104656114b6565b34801561099157600080fd5b506104ab6114c5565b6103a4600480360360c08110156109b057600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135600160201b8111156109ef57600080fd5b820183602082011115610a0157600080fd5b803590602001918460208302840111600160201b83111715610a2257600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050913592506114cb915050565b348015610a6e57600080fd5b50610ab1600480360360a0811015610a8557600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608001356115b6565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610aed578181015183820152602001610ad5565b505050509050019250505060405180910390f35b348015610b0d57600080fd5b5061046561164a565b348015610b2257600080fd5b506104ab611659565b348015610b3757600080fd5b5061046561165e565b348015610b4c57600080fd5b5061046561166d565b601e546040805163085e2c5b60e01b81526001600160a01b03888116600483015287811660248301526044820187905260648201869052608482018590529151600093606093169163085e2c5b9160a48083019287929190829003018186803b158015610bc157600080fd5b505afa158015610bd5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610bfe57600080fd5b815160208301805160405192949293830192919084600160201b821115610c2457600080fd5b908301906020820185811115610c3957600080fd5b82518660208202830111600160201b82111715610c5557600080fd5b82525081516020918201928201910280838360005b83811015610c82578181015183820152602001610c6a565b50505050905001604052505050915091509550959350505050565b600a546001600160a01b031681565b600c546001600160a01b031681565b61200081565b61800081565b64020000000081565b6011546001600160a01b031681565b601d546001600160a01b031681565b6204f00f81565b600160201b81565b608081565b6002546001600160a01b031681565b604081565b6010546001600160a01b031681565b6004546001600160a01b031681565b6001546001600160a01b031681565b6000610d57826001600160a01b031661167c565b15610d6557506000196110b6565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b178152915181516000936060936001600160a01b0388169361138893919290918291908083835b60208310610dd05780518252601f199092019160209182019101610db1565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114610e31576040519150601f19603f3d011682016040523d82523d6000602084013e610e36565b606091505b509150915081610e4c57600019925050506110b6565b6000805b6007835103811015610fa557828160000181518110610e6b57fe5b6020910101516001600160f81b031916602360f91b148015610eae5750828160010181518110610e9757fe5b6020910101516001600160f81b031916607560f81b145b8015610edb5750828160020181518110610ec457fe5b6020910101516001600160f81b031916601b60fa1b145b8015610f085750828160030181518110610ef157fe5b6020910101516001600160f81b031916606360f81b145b8015610f355750828160040181518110610f1e57fe5b6020910101516001600160f81b031916603960f91b145b8015610f625750828160050181518110610f4b57fe5b6020910101516001600160f81b031916607560f81b145b8015610f8f5750828160060181518110610f7857fe5b6020910101516001600160f81b031916606d60f81b145b15610f9d5760019150610fa5565b600101610e50565b5080610fb85760001993505050506110b6565b60408051600481526024810182526020810180516001600160e01b031663797bf38560e01b178152915181516001600160a01b038916936113889392918291908083835b6020831061101b5780518252601f199092019160209182019101610ffc565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d806000811461107c576040519150601f19603f3d011682016040523d82523d6000602084013e611081565b606091505b509093509150826110995760001993505050506110b6565b8180602001905160208110156110ae57600080fd5b505193505050505b919050565b600f546001600160a01b031681565b601081565b602081565b6006546001600160a01b031681565b600d546001600160a01b031681565b61400081565b61080081565b600881565b6202000081565b6014546001600160a01b031681565b60606111288787878686610b55565b9150506111398787878785876114cb565b50505050505050565b6208000081565b600061115d826001600160a01b031661167c565b1561116b57506000196110b6565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b178152915181516000936060936001600160a01b0388169361138893919290918291908083835b602083106111d65780518252601f1990920191602091820191016111b7565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114611237576040519150601f19603f3d011682016040523d82523d6000602084013e61123c565b606091505b50915091508161125257600019925050506110b6565b6000805b60048351038110156113245782816000018151811061127157fe5b6020910101516001600160f81b031916604160f81b1480156112b4575082816001018151811061129d57fe5b6020910101516001600160f81b031916606160f81b145b80156112e157508281600201815181106112ca57fe5b6020910101516001600160f81b031916603b60f91b145b801561130e57508281600301815181106112f757fe5b6020910101516001600160f81b031916606560f81b145b1561131c5760019150611324565b600101611256565b50806113375760001993505050506110b6565b60408051600481526024810182526020810180516001600160e01b0316632274683f60e21b178152915181516001600160a01b038916936113889392918291908083836020831061101b5780518252601f199092019160209182019101610ffc565b61040081565b6003546001600160a01b031681565b600281565b6015546001600160a01b031681565b6007546001600160a01b031681565b6009546001600160a01b031681565b620cfdff81565b60003330146113f557600080fd5b6114008484846116b6565b90505b9392505050565b6017546001600160a01b031681565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b6012546001600160a01b031681565b6013546001600160a01b031681565b62080df081565b600181565b61020081565b601c546001600160a01b031681565b33301461148b57600080fd5b6114958282611c05565b5050565b6204000081565b600e546001600160a01b031681565b6201000081565b6008546001600160a01b031681565b61010081565b3330146114ed576114ed6001600160a01b03871633308763ffffffff611cba16565b6114fa8686868585611dd9565b60006115156001600160a01b0387163063ffffffff611e0c16565b9050838110156115565760405162461bcd60e51b815260040180806020018281038252603581526020018061653f6035913960400191505060405180910390fd5b333014611139576115776001600160a01b038716338363ffffffff611eb616565b506115ac336115956001600160a01b038a163063ffffffff611e0c16565b6001600160a01b038a16919063ffffffff611eb616565b5050505050505050565b6060826040519080825280602002602001820160405280156115e2578160200160208202803883390190505b50905060005b838110156116405761162087876116188761160c8a6001880163ffffffff611f3116565b9063ffffffff611f8a16565b600187610b55565b5082828151811061162d57fe5b60209081029190910101526001016115e8565b5095945050505050565b6000546001600160a01b031681565b600481565b6016546001600160a01b031681565b601e546001600160a01b031681565b60006001600160a01b03821615806116b057506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b92915050565b60006116ca846001600160a01b031661167c565b1561173957600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561171f57600080fd5b505af1158015611733573d6000803e3d6000fd5b50505050505b600a5460408051632ecd14d360e21b81526c42616e636f724e6574776f726b60981b600482015290516000926001600160a01b03169163bb34534c916024808301926020929190829003018186803b15801561179457600080fd5b505afa1580156117a8573d6000803e3d6000fd5b505050506040513d60208110156117be57600080fd5b5051600b549091506060906001600160a01b03908116906375e1cc82906117e690891661167c565b6117f057876117fd565b6007546001600160a01b03165b61180f886001600160a01b031661167c565b6118195787611826565b6007546001600160a01b03165b6040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b03168152602001826001600160a01b03166001600160a01b031681526020019250505060006040518083038186803b15801561188557600080fd5b505afa158015611899573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156118c257600080fd5b8101908080516040519392919084600160201b8211156118e157600080fd5b9083019060208201858111156118f657600080fd5b82518660208202830111600160201b8211171561191257600080fd5b82525081516020918201928201910280838360005b8381101561193f578181015183820152602001611927565b505050509050016040525050509050611980611963876001600160a01b031661167c565b61196d578661197a565b6007546001600160a01b03165b83611c05565b60006060836001600160a01b03166216e360856001600160a01b031663c7ba24bc905060e01b8589600160405160240180806020018481526020018360ff168152602001828103825285818151815260200191508051906020019060200280838360005b838110156119fc5781810151838201526020016119e4565b50505050905001945050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0383818316178352505050506040518082805190602001908083835b60208310611a6b5780518252601f199092019160209182019101611a4c565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038160008787f1925050503d8060008114611ace576040519150601f19603f3d011682016040523d82523d6000602084013e611ad3565b606091505b5091509150600082611ae6576000611afe565b818060200190516020811015611afb57600080fd5b50515b9050611b12886001600160a01b031661167c565b8015611b1e5750600081115b15611bf957600754604080516370a0823160e01b815230600482015290516001600160a01b0390921691632e1a7d4d9183916370a0823191602480820192602092909190829003018186803b158015611b7657600080fd5b505afa158015611b8a573d6000803e3d6000fd5b505050506040513d6020811015611ba057600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015611be057600080fd5b505af1158015611bf4573d6000803e3d6000fd5b505050505b98975050505050505050565b611c17826001600160a01b031661167c565b6114955760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b158015611c6c57600080fd5b505afa158015611c80573d6000803e3d6000fd5b505050506040513d6020811015611c9657600080fd5b5051901c611495576114956001600160a01b0383168260001963ffffffff611fcc16565b80611cc457611dd3565b611ccd8461167c565b15611db8576001600160a01b03831633148015611cea5750803410155b611d255760405162461bcd60e51b815260040180806020018281038252602b8152602001806165a3602b913960400191505060405180910390fd5b6001600160a01b0382163014611d6d576040516001600160a01b0383169082156108fc029083906000818181858888f19350505050158015611d6b573d6000803e3d6000fd5b505b80341115611db357336108fc611d89348463ffffffff6120a216565b6040518115909202916000818181858888f19350505050158015611db1573d6000803e3d6000fd5b505b611dd3565b611dd36001600160a01b03851684848463ffffffff6120e416565b50505050565b836001600160a01b0316856001600160a01b03161415611df857611e05565b611e05858585858561213e565b5050505050565b6000611e178361167c565b15611e2d57506001600160a01b038116316116b0565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015611e8357600080fd5b505afa158015611e97573d6000803e3d6000fd5b505050506040513d6020811015611ead57600080fd5b50519392505050565b600081611ec557506001611403565b611ece8461167c565b15611f0f576040516001600160a01b0384169083156108fc029084906000818181858888f19350505050158015611f09573d6000803e3d6000fd5b50611403565b611f296001600160a01b038516848463ffffffff6124c716565b506001611403565b600082611f40575060006116b0565b82820282848281611f4d57fe5b04146114035760405162461bcd60e51b81526004018080602001828103825260218152602001806165ce6021913960400191505060405180910390fd5b600061140383836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612519565b611fd58361167c565b61209d57600081118015612063575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561203557600080fd5b505afa158015612049573d6000803e3d6000fd5b505050506040513d602081101561205f57600080fd5b5051115b15612083576120836001600160a01b03841683600063ffffffff6125bb16565b61209d6001600160a01b038416838363ffffffff6125bb16565b505050565b600061140383836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506126ce565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052611dd3908590612728565b836001600160a01b0316856001600160a01b0316141561215d57611e05565b61216f8161010063ffffffff6128e016565b6124ba57601854604080516302091f7b60e51b81526001600160a01b03888116600483015291516000939290921691634123ef6091602480820192602092909190829003018186803b1580156121c457600080fd5b505afa1580156121d8573d6000803e3d6000fd5b505050506040513d60208110156121ee57600080fd5b5051601854604080516302091f7b60e51b81526001600160a01b03898116600483015291519394506000939190921691634123ef60916024808301926020929190829003018186803b15801561224357600080fd5b505afa158015612257573d6000803e3d6000fd5b505050506040513d602081101561226d57600080fd5b5051905081801561227b5750805b1561248857606084516040519080825280602002602001820160405280156122ad578160200160208202803883390190505b50905060005b8551811015612301578581815181106122c857fe5b60200260200101516fffffffffffffffffffffffffffffffff168282815181106122ee57fe5b60209081029190910101526001016122b3565b50601a54604080516370a0823160e01b815230600482015290516000926001600160a01b0316916370a08231916024808301926020929190829003018186803b15801561234d57600080fd5b505afa158015612361573d6000803e3d6000fd5b505050506040513d602081101561237757600080fd5b5051601a54909150612397908a906001600160a01b0316898560006128e6565b60005b86518110156123da5760808782815181106123b157fe5b6020026020010151901c8382815181106123c757fe5b602090810291909101015260010161239a565b50601a54604080516370a0823160e01b815230600482015290516000926001600160a01b0316916370a08231916024808301926020929190829003018186803b15801561242657600080fd5b505afa15801561243a573d6000803e3d6000fd5b505050506040513d602081101561245057600080fd5b5051601a5490915061247e906001600160a01b03168a612476848663ffffffff6120a216565b866000612bad565b5050505050611e05565b81156124a35761249c8787878760006128e6565b5050611e05565b80156124b75761249c878787876000612bad565b50505b611e058585858585613395565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261209d908490612728565b600081836125a55760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561256a578181015183820152602001612552565b50505050905090810190601f1680156125975780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816125b157fe5b0495945050505050565b801580612641575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b15801561261357600080fd5b505afa158015612627573d6000803e3d6000fd5b505050506040513d602081101561263d57600080fd5b5051155b61267c5760405162461bcd60e51b81526004018080602001828103825260368152602001806166196036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261209d908490612728565b600081848411156127205760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561256a578181015183820152602001612552565b505050900390565b61273a826001600160a01b03166133a2565b61278b576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106127c95780518252601f1990920191602091820191016127aa565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461282b576040519150601f19603f3d011682016040523d82523d6000602084013e612830565b606091505b509150915081612887576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115611dd3578080602001905160208110156128a357600080fd5b5051611dd35760405162461bcd60e51b815260040180806020018281038252602a8152602001806165ef602a913960400191505060405180910390fd5b16151590565b6128ee6164b3565b6128f7866133de565b905080602001516001600160a01b031663415f1240856040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561294357600080fd5b505af1158015612957573d6000803e3d6000fd5b5050505060608351604051908082528060200260200182016040528015612988578160200160208202803883390190505b50905060005b8251518110156115ac57866001600160a01b0316836000015182815181106129b257fe5b6020026020010151600001516001600160a01b031614156129d257612ba5565b60005b8551811015612a1a57816008028682815181106129ee57fe5b6020026020010151901c60ff16838281518110612a0757fe5b60209081029190910101526001016129d5565b50306001600160a01b031663e2a7515e612a4e85600001518481518110612a3d57fe5b60200260200101516000015161362a565b8986600001518581518110612a5f57fe5b60209081029190910181015151604080516370a0823160e01b815230600482015290516001600160a01b03909216926370a0823192602480840193829003018186803b158015612aae57600080fd5b505afa158015612ac2573d6000803e3d6000fd5b505050506040513d6020811015612ad857600080fd5b50516040516001600160e01b031960e086901b1681526001600160a01b038085166004830190815290841660248301526044820183905260006064830181905260a483018c905260c0608484019081528a5160c48501528a5191938b938e9390929160e40190602086810191028083838a5b83811015612b62578181015183820152602001612b4a565b50505050905001975050505050505050600060405180830381600087803b158015612b8c57600080fd5b505af1158015612ba0573d6000803e3d6000fd5b505050505b60010161298e565b60608251604051908082528060200260200182016040528015612bda578160200160208202803883390190505b509050600019612be86164b3565b612bf1876133de565b90506000805b8251518110156131be576000612c39846040015161160c86600001518581518110612c1e57fe5b6020026020010151602001518c611f3190919063ffffffff16565b90508a6001600160a01b031684600001518381518110612c5557fe5b6020026020010151600001516001600160a01b031614612fcc57600084600001518381518110612c8157fe5b60209081029190910181015151604080516370a0823160e01b815230600482015290516001600160a01b03909216926370a0823192602480840193829003018186803b158015612cd057600080fd5b505afa158015612ce4573d6000803e3d6000fd5b505050506040513d6020811015612cfa57600080fd5b5051905060005b8951811015612d4657836008028a8281518110612d1a57fe5b6020026020010151901c60ff16888281518110612d3357fe5b6020908102919091010152600101612d01565b50612d668c612d5e87600001518681518110612a3d57fe5b848a8c613395565b600085600001518481518110612d7857fe5b60209081029190910181015151604080516370a0823160e01b815230600482015290516001600160a01b03909216926370a0823192602480840193829003018186803b158015612dc757600080fd5b505afa158015612ddb573d6000803e3d6000fd5b505050506040513d6020811015612df157600080fd5b81019080805190602001909291905050509050601960009054906101000a90046001600160a01b03166001600160a01b03166329a00e7c8d6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612e6157600080fd5b505afa158015612e75573d6000803e3d6000fd5b505050506040513d6020811015612e8b57600080fd5b50518851805188908110612e9b57fe5b6020026020010151600001516001600160a01b03166370a082318a602001516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015612f0057600080fd5b505afa158015612f14573d6000803e3d6000fd5b505050506040513d6020811015612f2a57600080fd5b505160408a0151612f41868863ffffffff6120a216565b6040518563ffffffff1660e01b8152600401808581526020018481526020018363ffffffff1663ffffffff16815260200182815260200194505050505060206040518083038186803b158015612f9657600080fd5b505afa158015612faa573d6000803e3d6000fd5b505050506040513d6020811015612fc057600080fd5b50519450613180915050565b601960009054906101000a90046001600160a01b03166001600160a01b03166329a00e7c8b6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561302957600080fd5b505afa15801561303d573d6000803e3d6000fd5b505050506040513d602081101561305357600080fd5b5051865180518690811061306357fe5b6020026020010151600001516001600160a01b03166370a0823188602001516040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156130c857600080fd5b505afa1580156130dc573d6000803e3d6000fd5b505050506040513d60208110156130f257600080fd5b505160408089015181516001600160e01b031960e087901b1681526004810194909452602484019290925263ffffffff909116604483015260648201859052516084808301926020929190829003018186803b15801561315157600080fd5b505afa158015613165573d6000803e3d6000fd5b505050506040513d602081101561317b57600080fd5b505192505b8483101561318c578294505b6131b58460000151838151811061319f57fe5b6020026020010151600001518560200151611c05565b50600101612bf7565b5081602001516001600160a01b031663ca1d209d846040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561320957600080fd5b505af115801561321d573d6000803e3d6000fd5b506000925050505b82515181101561338957600061324484600001518381518110612a3d57fe5b905060008460000151838151811061325857fe5b60209081029190910181015151604080516370a0823160e01b815230600482015290516001600160a01b03909216926370a0823192602480840193829003018186803b1580156132a757600080fd5b505afa1580156132bb573d6000803e3d6000fd5b505050506040513d60208110156132d157600080fd5b5051604080516309be685f60e41b81526001600160a01b0385811660048301528e1660248201526044810183905290519192506000913091639be685f091606480830192602092919082900301818787803b15801561332f57600080fd5b505af1158015613343573d6000803e3d6000fd5b505050506040513d602081101561335957600080fd5b505190508061337e5761337c6001600160a01b038416338463ffffffff611eb616565b505b505050600101613225565b50505050505050505050565b611e058585858585613659565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906133d657508115155b949350505050565b6133e66164b3565b6000826001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561342157600080fd5b505afa158015613435573d6000803e3d6000fd5b505050506040513d602081101561344b57600080fd5b50516001600160a01b0381166020848101829052604080516371f52bf360e01b8152905193945091926371f52bf3926004808201939291829003018186803b15801561349657600080fd5b505afa1580156134aa573d6000803e3d6000fd5b505050506040513d60208110156134c057600080fd5b50516040805182815260208084028201019091529080156134fb57816020015b6134e86164dd565b8152602001906001900390816134e05790505b50825260005b82515181101561362357816001600160a01b03166319b64015826040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561354f57600080fd5b505afa158015613563573d6000803e3d6000fd5b505050506040513d602081101561357957600080fd5b5051835180518390811061358957fe5b60209081029190910101516001600160a01b039091169052825180516135c6918491849081106135b557fe5b602002602001015160000151613938565b83518051839081106135d457fe5b60200260200101516020018181525050613616836000015182815181106135f757fe5b6020026020010151602001518460400151613ae190919063ffffffff16565b6040840152600101613501565b5050919050565b601d546000906001600160a01b0383811691161461364857816116b0565b5050601c546001600160a01b031690565b836001600160a01b0316856001600160a01b0316141561367857611e05565b61368b816208000063ffffffff6128e016565b61392b576006546001600160a01b03868116911614156137a057600654604080516370a0823160e01b815230600482015290516001600160a01b0390921691632e1a7d4d9183916370a0823191602480820192602092909190829003018186803b1580156136f857600080fd5b505afa15801561370c573d6000803e3d6000fd5b505050506040513d602081101561372257600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561376257600080fd5b505af1158015613776573d6000803e3d6000fd5b5050505061379b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee85858585613b3b565b611e05565b6007546001600160a01b038681169116141561380957600754604080516370a0823160e01b815230600482015290516001600160a01b0390921691632e1a7d4d9183916370a0823191602480820192602092909190829003018186803b1580156136f857600080fd5b6006546001600160a01b03858116911614156138a4576138408573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585613659565b600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b15801561389057600080fd5b505af115801561247e573d6000803e3d6000fd5b6007546001600160a01b038581169116141561392b576138db8573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585613659565b600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b15801561389057600080fd5b611e058585858585613b3b565b604080516001600160a01b0383811660248084019190915283518084039091018152604490920183526020820180516001600160e01b03166316095a2360e11b17815292518251600094859460609489169361271093919290918291908083835b602083106139b85780518252601f199092019160209182019101613999565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613a19576040519150601f19603f3d011682016040523d82523d6000602084013e613a1e565b606091505b50915091508115613a4a57808060200190516020811015613a3e57600080fd5b505192506116b0915050565b6000856001600160a01b0316630e53aae9866040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060a06040518083038186803b158015613aa257600080fd5b505afa158015613ab6573d6000803e3d6000fd5b505050506040513d60a0811015613acc57600080fd5b506020015163ffffffff169695505050505050565b600082820183811015611403576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b611e0585858585855b836001600160a01b0316856001600160a01b03161415613b6357611e05565b613b6b6164f4565b613b73613f3b565b9050613b878261080063ffffffff6128e016565b613f265760005b600a811015613d46578181600a8110613ba357fe5b60200201516001600160a01b0316876001600160a01b03161415613d3e5760008282600a8110613bcf57fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015613c0c57600080fd5b505afa158015613c20573d6000803e3d6000fd5b505050506040513d6020811015613c3657600080fd5b505190508282600a8110613c4657fe5b60200201516001600160a01b0316632e1a7d4d876040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015613c9057600080fd5b505af1158015613ca4573d6000803e3d6000fd5b50505050613d368188836001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015613d0357600080fd5b505afa158015613d17573d6000803e3d6000fd5b505050506040513d6020811015613d2d57600080fd5b50518888613b44565b505050611e05565b600101613b8e565b5060005b600a811015613f24578181600a8110613d5f57fe5b60200201516001600160a01b0316866001600160a01b03161415613f1c5760008282600a8110613d8b57fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015613dc857600080fd5b505afa158015613ddc573d6000803e3d6000fd5b505050506040513d6020811015613df257600080fd5b50519050613e038882888888614057565b613e1d818484600a8110613e1357fe5b6020020151611c05565b8282600a8110613e2957fe5b60200201516001600160a01b031663b6b55f25826001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015613e9257600080fd5b505afa158015613ea6573d6000803e3d6000fd5b505050506040513d6020811015613ebc57600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015613efc57600080fd5b505af1158015613f10573d6000803e3d6000fd5b50505050505050611e05565b600101613d4a565b505b613f338686868686614057565b505050505050565b613f436164f4565b5060408051610140810182527316de59092dae5ccf4a1e6439d611fd0653f0bd0181527304aa51bbcb46541455ccf1b8bef2ebc5d3787ec960208201527373a052500105205d34daf004eab301916da8190f918101919091527383f798e925bcd4017eb265844fddabb448f1707d606082015273d6ad7a6750a7593e092a9b218d66c0a814a3436e608082015273f61718057901f84c4eec4339ef8f0d86d2b4560060a08201527304bc0ab673d88ae9dbc9da2380cb6b79c4bca9ae60c082015273c2cb1040220768554cf699b0d863a3cd4324ce3260e082015273e6354ed5bc4b393a5aad09f21c46e101e692d4476101008201527326ea744e5b887e5205727f55dfbe8685e3b2195161012082015290565b611e0585858585855b836001600160a01b0316856001600160a01b0316141561407f57611e05565b61409081601063ffffffff6128e016565b6142995761409d856142a6565b1561414c5760006140ad866143d9565b9050856001600160a01b031663db006a75856040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156140f557600080fd5b505af1158015614109573d6000803e3d6000fd5b505050506040513d602081101561411f57600080fd5b506000905061413d6001600160a01b0383163063ffffffff611e0c16565b905061249c8287838787614060565b614155846142a6565b15614299576000614165856143d9565b90506141748682868686614500565b600061418f6001600160a01b0383163063ffffffff611e0c16565b90506141a3826001600160a01b031661167c565b1561421657601760009054906101000a90046001600160a01b03166001600160a01b0316631249c58b826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156141f857600080fd5b505af115801561420c573d6000803e3d6000fd5b505050505061249c565b6142208287611c05565b856001600160a01b031663a0712d68826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561426657600080fd5b505af115801561427a573d6000803e3d6000fd5b505050506040513d602081101561429057600080fd5b50505050611e05565b611e058585858585614500565b6017546000906001600160a01b03838116911614156142c7575060016110b6565b601654604080516001600160a01b0385811660248084019190915283518084039091018152604490920183526020820180516001600160e01b0316638e8f294b60e01b17815292518251600095606095931693611388939092918291908083835b602083106143475780518252601f199092019160209182019101614328565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d80600081146143a8576040519150601f19603f3d011682016040523d82523d6000602084013e6143ad565b606091505b5091509150816143c2576000925050506110b6565b60008180602001905160408110156110ae57600080fd5b6017546000906001600160a01b03838116911614156143fa575060006110b6565b60408051600481526024810182526020810180516001600160e01b0316636f307dc360e01b178152915181516000936060936001600160a01b0388169361138893919290918291908083835b602083106144655780518252601f199092019160209182019101614446565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d80600081146144c6576040519150601f19603f3d011682016040523d82523d6000602084013e6144cb565b606091505b5091509150816144e157600019925050506110b6565b8080602001905160208110156144f657600080fd5b5051949350505050565b611e058585858585836001600160a01b0316856001600160a01b0316141561452757611e05565b61453881602063ffffffff6128e016565b6147cb57600061454786610d43565b90506001600160a01b03808216146146975761456b816001600160a01b031661167c565b156145f1576040805163081a6b2560e41b81523060048201526024810186905290516001600160a01b038816916381a6b2509160448083019260209291908290030181600087803b1580156145bf57600080fd5b505af11580156145d3573d6000803e3d6000fd5b505050506040513d60208110156145e957600080fd5b5061466d9050565b60408051632770a7eb60e21b81523060048201526024810186905290516001600160a01b03881691639dc29fac9160448083019260209291908290030181600087803b15801561464057600080fd5b505af1158015614654573d6000803e3d6000fd5b505050506040513d602081101561466a57600080fd5b50505b60006146886001600160a01b0383163063ffffffff611e0c16565b905061249c82878387876147d4565b6146a085610d43565b90506001600160a01b03808216146147c9576146bf86828686866147d4565b60006146da6001600160a01b0383163063ffffffff611e0c16565b90506146ee826001600160a01b031661167c565b156147705760408051638f6ede1f60e01b815230600482015290516001600160a01b03881691638f6ede1f91849160248082019260209290919082900301818588803b15801561473d57600080fd5b505af1158015614751573d6000803e3d6000fd5b50505050506040513d602081101561476857600080fd5b5061249c9050565b61477a8287611c05565b604080516340c10f1960e01b81523060048201526024810183905290516001600160a01b038816916340c10f199160448083019260209291908290030181600087803b15801561426657600080fd5b505b611e0585858585855b611e0585858585855b836001600160a01b0316856001600160a01b031614156147fc57611e05565b61480d81608063ffffffff6128e016565b614a1d57600061481c86611149565b90506001600160a01b038082161461489f57856001600160a01b031663db006a75856040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561487457600080fd5b505af1158015614888573d6000803e3d6000fd5b5050505061489981868686866147dd565b50611e05565b6148a885611149565b90506001600160a01b0380821614614a1b576148c78682868686614a26565b60006148e26001600160a01b0383163063ffffffff611e0c16565b905061496782601560009054906101000a90046001600160a01b03166001600160a01b031663f2f4eb266040518163ffffffff1660e01b815260040160206040518083038186803b15801561493657600080fd5b505afa15801561494a573d6000803e3d6000fd5b505050506040513d602081101561496057600080fd5b5051611c05565b6015546001600160a01b039081169063d2d0e0669061498790851661167c565b614992576000614994565b825b6149a6856001600160a01b031661167c565b6149b057846149c6565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e086901b1681526001600160a01b0390921660048301526024820186905261044d604483015251606480830192600092919082900301818588803b158015613efc57600080fd5b505b611e0585858585855b836001600160a01b0316856001600160a01b03161415614a4557611e05565b614a578161040063ffffffff6128e016565b614ccb576013546001600160a01b0386811691161415614bac5760135460408051637f8661a160e01b81526004810186905290516001600160a01b0390921691637f8661a19160248082019260009290919082900301818387803b158015614abe57600080fd5b505af1158015614ad2573d6000803e3d6000fd5b5050601454604080516370a0823160e01b81523060048201529051600094506001600160a01b0390921692506370a08231916024808301926020929190829003018186803b158015614b2357600080fd5b505afa158015614b37573d6000803e3d6000fd5b505050506040513d6020811015614b4d57600080fd5b505190508015614b9357601454606090614b74906001600160a01b03168784600187610b55565b601454909250614b9191506001600160a01b031687848487611dd9565b505b600054614899906001600160a01b031686868686614cd4565b6013546001600160a01b0385811691161415614ccb57600054614bdc9086906001600160a01b0316858585614cd4565b600054601354614bf8916001600160a01b039081169116611c05565b601354600054604080516370a0823160e01b815230600482015290516001600160a01b039384169363049878f39316916370a08231916024808301926020929190829003018186803b158015614c4d57600080fd5b505afa158015614c61573d6000803e3d6000fd5b505050506040513d6020811015614c7757600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015614cb757600080fd5b505af1158015614290573d6000803e3d6000fd5b611e0585858585855b836001600160a01b0316856001600160a01b03161415614cf357611e05565b614d0481604063ffffffff6128e016565b614f2b576012546001600160a01b0386811691161415614e0e576012546040805163ef693bed60e01b81523060048201526024810186905290516001600160a01b039092169163ef693bed9160448082019260009290919082900301818387803b158015614d7157600080fd5b505af1158015614d85573d6000803e3d6000fd5b5050600054604080516370a0823160e01b8152306004820152905161379b94506001600160a01b039092169250879183916370a08231916024808301926020929190829003018186803b158015614ddb57600080fd5b505afa158015614def573d6000803e3d6000fd5b505050506040513d6020811015614e0557600080fd5b50518585614f34565b6012546001600160a01b0385811691161415614f2b57600054614e3e9086906001600160a01b0316858585614f34565b600054601254614e5a916001600160a01b039081169116611c05565b601254600054604080516370a0823160e01b8152306004820181905291516001600160a01b0394851694633b4da69f9416916370a08231916024808301926020929190829003018186803b158015614eb157600080fd5b505afa158015614ec5573d6000803e3d6000fd5b505050506040513d6020811015614edb57600080fd5b5051604080516001600160e01b031960e086901b1681526001600160a01b039093166004840152602483019190915251604480830192600092919082900301818387803b158015614cb757600080fd5b611e0585858585855b614f46856001600160a01b031661167c565b158015614f625750614f60846001600160a01b031661167c565b155b8015614f7b5750614f7b8161020063ffffffff6128e016565b1561507c5760608251604051908082528060200260200182016040528015614fad578160200160208202803883390190505b50905060005b8351811015614ff257838181518110614fc857fe5b602002602001015160ff16828281518110614fdf57fe5b6020908102919091010152600101614fb3565b506150148673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee868486615396565b60005b835181101561505a57600884828151811061502e57fe5b6020026020010151901c60ff1682828151811061504757fe5b6020908102919091010152600101615017565b5061489973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee86478486615396565b6000546001600160a01b038681169116148015906150a857506000546001600160a01b03858116911614155b80156150c257506150c2816201000063ffffffff6128e016565b1561521e57606082516040519080825280602002602001820160405280156150f4578160200160208202803883390190505b50905060005b83518110156151395783818151811061510f57fe5b602002602001015160ff1682828151811061512657fe5b60209081029190910101526001016150fa565b506000546151549087906001600160a01b0316868486615396565b60005b835181101561519a57600884828151811061516e57fe5b6020026020010151901c60ff1682828151811061518757fe5b6020908102919091010152600101615157565b50600054604080516370a0823160e01b81523060048201529051614899926001600160a01b031691889183916370a08231916024808301926020929190829003018186803b1580156151eb57600080fd5b505afa1580156151ff573d6000803e3d6000fd5b505050506040513d602081101561521557600080fd5b50518486615396565b6001546001600160a01b0386811691161480159061524a57506001546001600160a01b03858116911614155b80156152645750615264816202000063ffffffff6128e016565b1561538d5760608251604051908082528060200260200182016040528015615296578160200160208202803883390190505b50905060005b83518110156152db578381815181106152b157fe5b602002602001015160ff168282815181106152c857fe5b602090810291909101015260010161529c565b506001546152f69087906001600160a01b0316868486615396565b60005b835181101561533c57600884828151811061531057fe5b6020026020010151901c60ff1682828151811061532957fe5b60209081029190910101526001016152f9565b50600154604080516370a0823160e01b81523060048201529051614899926001600160a01b031691889183916370a08231916024808301926020929190829003018186803b1580156151eb57600080fd5b611e0585858585855b836001600160a01b0316856001600160a01b031614156153b557611e05565b6153bd616513565b50604080516101208101825261554581526157a96020820152615918918101919091526159356060820152615bda6080820152615d3a60a0820152615ebc60c082015261608d60e0820152616262610100820152600080805b600981101561546f57600086828151811061542d57fe5b602002602001015111156154675761546186828151811061544a57fe5b602002602001015184613ae190919063ffffffff16565b92508091505b600101615416565b50600082116154af5760405162461bcd60e51b815260040180806020018281038252602f815260200180616574602f913960400191505060405180910390fd5b8560005b6009811015613389578681815181106154c857fe5b6020026020010151600014156154dd5761553d565b60006155098561160c8a85815181106154f257fe5b60200260200101518c611f3190919063ffffffff16565b9050838214156155165750815b808303925061553a8b8b8389866009811061552d57fe5b602002015163ffffffff16565b50505b6001016154b3565b60008161555a6001600160a01b03861661167c565b61567d57600954604080516303795fb160e11b81526001600160a01b038881166004830152915160009392909216916306f2bf6291602480820192602092909190829003018186803b1580156155af57600080fd5b505afa1580156155c3573d6000803e3d6000fd5b505050506040513d60208110156155d957600080fd5b505190506001600160a01b0381161561567b576155f68682611c05565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561564c57600080fd5b505af1158015615660573d6000803e3d6000fd5b505050506040513d602081101561567657600080fd5b505191505b505b61568f846001600160a01b031661167c565b61140057600954604080516303795fb160e11b81526001600160a01b038781166004830152915160009392909216916306f2bf6291602480820192602092909190829003018186803b1580156156e457600080fd5b505afa1580156156f8573d6000803e3d6000fd5b505050506040513d602081101561570e57600080fd5b505190506001600160a01b038116156157a057806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b15801561577057600080fd5b505af1158015615784573d6000803e3d6000fd5b50505050506040513d602081101561579b57600080fd5b505191505b50949350505050565b6008546000906157c39085906001600160a01b0316611c05565b6008546001600160a01b03908116906329589f61906157e390871661167c565b6157ee5760006157f0565b835b615802876001600160a01b031661167c565b61580c5786615822565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b85615835886001600160a01b031661167c565b61583f5787615855565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b1580156158ed57600080fd5b505af1158015615901573d6000803e3d6000fd5b50505050506040513d60208110156144f657600080fd5b6000806159268585856116b6565b90506000811161140057600080fd5b6000615949846001600160a01b031661167c565b156159b857600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561599e57600080fd5b505af11580156159b2573d6000803e3d6000fd5b50505050505b6159f56159cd856001600160a01b031661167c565b6159d757846159e4565b6006546001600160a01b03165b600c546001600160a01b0316611c05565b600c546000906001600160a01b0390811690630621b4f690615a1890881661167c565b615a225786615a2f565b6006546001600160a01b03165b85615a42886001600160a01b031661167c565b615a4c5787615a59565b6006546001600160a01b03165b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b158015615ab757600080fd5b505af1158015615acb573d6000803e3d6000fd5b505050506040513d6020811015615ae157600080fd5b50519050615af76001600160a01b03851661167c565b1561140057600654604080516370a0823160e01b815230600482015290516001600160a01b0390921691632e1a7d4d9183916370a0823191602480820192602092909190829003018186803b158015615b4f57600080fd5b505afa158015615b63573d6000803e3d6000fd5b505050506040513d6020811015615b7957600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015615bb957600080fd5b505af1158015615bcd573d6000803e3d6000fd5b5050505090509392505050565b60015460009081906001600160a01b03868116911614615bfb576000615bfe565b60025b6000546001600160a01b03878116911614615c1a576000615c1d565b60015b600154910160ff1691506000906001600160a01b0390811690861614615c44576000615c47565b60025b6000546001600160a01b03878116911614615c63576000615c66565b60015b0160ff16905081600f0b60001480615c81575080600f0b6000145b15615c9157600092505050611403565b600d54615ca89087906001600160a01b0316611c05565b600d5460408051635320bf6b60e11b8152600019808601600f90810b810b6004840152908501810b900b60248201526044810187905260006064820181905291516001600160a01b039093169263a6417ed69260848084019391929182900301818387803b158015615d1957600080fd5b505af1158015615d2d573d6000803e3d6000fd5b5050505050509392505050565b60025460009081906001600160a01b03868116911614615d5b576000615d5e565b60035b6001546001600160a01b03878116911614615d7a576000615d7d565b60025b6000546001600160a01b03888116911614615d99576000615d9c565b60015b60025491019190910160ff1691506000906001600160a01b0390811690861614615dc7576000615dca565b60035b6001546001600160a01b03878116911614615de6576000615de9565b60025b6000546001600160a01b03888116911614615e05576000615e08565b60015b010160ff16905081600f0b60001480615e24575080600f0b6000145b15615e3457600092505050611403565b600e54615e4b9087906001600160a01b0316611c05565b600e5460408051635320bf6b60e11b8152600019808601600f90810b810b6004840152908501810b900b60248201526044810187905260006064820181905291516001600160a01b039093169263a6417ed69260848084019391929182900301818387803b158015615d1957600080fd5b60035460009081906001600160a01b03868116911614615edd576000615ee0565b60045b6002546001600160a01b03878116911614615efc576000615eff565b60035b6001546001600160a01b03888116911614615f1b576000615f1e565b60025b6000546001600160a01b03898116911614615f3a576000615f3d565b60015b01010160ff1690506000600360009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b031614615f7c576000615f7f565b60045b6002546001600160a01b03878116911614615f9b576000615f9e565b60035b6001546001600160a01b03888116911614615fba576000615fbd565b60025b6000546001600160a01b03898116911614615fd9576000615fdc565b60015b01010160ff16905081600f0b60001480615ff9575080600f0b6000145b1561600957600092505050611403565b600f546160209087906001600160a01b0316611c05565b600f805460408051635320bf6b60e11b8152600019808701850b850b60048301528501840b90930b60248401526044830187905260006064840181905290516001600160a01b039092169263a6417ed6926084808301939282900301818387803b158015615d1957600080fd5b60045460009081906001600160a01b038681169116146160ae5760006160b1565b60045b6002546001600160a01b038781169116146160cd5760006160d0565b60035b6001546001600160a01b038881169116146160ec5760006160ef565b60025b6000546001600160a01b0389811691161461610b57600061610e565b60015b01010160ff1690506000600460009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b03161461614d576000616150565b60045b6002546001600160a01b0387811691161461616c57600061616f565b60035b6001546001600160a01b0388811691161461618b57600061618e565b60025b6000546001600160a01b038981169116146161aa5760006161ad565b60015b01010160ff16905081600f0b600014806161ca575080600f0b6000145b156161da57600092505050611403565b6010546161f19087906001600160a01b0316611c05565b60105460408051635320bf6b60e11b8152600019808601600f90810b810b6004840152908501810b900b60248201526044810187905260006064820181905291516001600160a01b039093169263a6417ed69260848084019391929182900301818387803b158015615d1957600080fd5b60055460009081906001600160a01b03868116911614616283576000616286565b60055b6003546001600160a01b038781169116146162a25760006162a5565b60045b6002546001600160a01b038881169116146162c15760006162c4565b60035b6001546001600160a01b038981169116146162e05760006162e3565b60025b6000546001600160a01b038a81169116146162ff576000616302565b60015b0101010160ff1690506000600560009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b031614616342576000616345565b60055b6003546001600160a01b03878116911614616361576000616364565b60045b6002546001600160a01b03888116911614616380576000616383565b60035b6001546001600160a01b0389811691161461639f5760006163a2565b60025b6000546001600160a01b038a81169116146163be5760006163c1565b60015b0101010160ff16905081600f0b600014806163df575080600f0b6000145b156163ef57600092505050611403565b6005546001600160a01b0387811691161480159061641b57506005546001600160a01b03868116911614155b1561642b57600092505050611403565b6011546164429087906001600160a01b0316611c05565b60115460408051635320bf6b60e11b8152600019808601600f90810b810b6004840152908501810b900b60248201526044810187905260006064820181905291516001600160a01b039093169263a6417ed69260848084019391929182900301818387803b158015615d1957600080fd5b60405180606001604052806060815260200160006001600160a01b03168152602001600081525090565b604080518082019091526000808252602082015290565b604051806101400160405280600a906020820280388339509192915050565b6040518061012001604052806009905b61653c8152602001906001900390816165235790505090565bfefe4f6e6553706c69743a2061637475616c2072657475726e20616d6f756e74206973206c657373207468616e206d696e52657475726e4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f7357726f6e6720757365616765206f66204554482e756e6976657273616c5472616e7366657246726f6d2829536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e6365a265627a7a72315820181695d831e5f656e86618f59a1f16e8b49edf4672a4198ae0257b8702135db164736f6c63430005100032 \ No newline at end of file +6080604052600080546001600160a01b0319908116736b175474e89094c44da98b954eedeac495271d0f1790915560018054821673a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4817905560028054821673dac17f958d2ee523a2206206994597c13d831ec71790556003805482166e085d4780b73119b644ae5ecd22b376179055600480548216734fabb145d64652a948d72533023f6e7a623c7c531790556005805482167357ab1ec28d129707052df4df418d58a2d46d5f5117905560068054821673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc217905560078054821673c0829421c1d260bd3cb3e0f06cfe2d52db2ce31517905560088054821673818e6fecd516ecc3849daf6845e3ec868087b75517905560098054821673c0a47dfe034b400b47bdad5fecda2621de6c4d95179055600a805482167352ae12abe5d8bd778bd5397f99ca900624cfadd4179055600b80548216736f0cd8c4f6f06eab664c7e3031909452b4b72861179055600c8054821673794e6e91555438afc3ccf1c5076a74f42133d08d179055600d8054821673a2b47e3d5c44877cca798226b7b8118f9bfb7a56179055600e805482167352ea46506b9cc5ef470c5bf89f17dc28bb35d85c179055600f805482167345f783cce6b7ff23b2ab2d70e416cdb7d6055f511790556010805482167379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27179055601180548216733b12e1fbb468bea80b492d635976809bf950186c1790556012805482167306af07097c9eeb7fd685c692751d5c66db49c215179055601380548216736a4ffaafa8dd400676df8076ad6c724867b0e2e817905560148054821673b683d83a532e2cb7dfa5275eed3698436371cc9f17905560158054821673398ec7346dcd622edc5ae82352f02be94c62d119179055601680548216733d9819210a31b4961b30ef54be2aed79b9c9cd3b179055601780548216734ddc2d193948926d02f9b1fe9e1daa0718270ed517905560188054821673f6e2d7f616b67e46d708e4410746e9aab3a4c5181790556019805490911673524619eb9b4cdffa7da13029b33f24635478afc01790553480156200032457600080fd5b5060405162006a6638038062006a66833981016040819052620003479162000380565b601a80546001600160a01b0319166001600160a01b0392909216919091179055620003e9565b80516200037a81620003cf565b92915050565b6000602082840312156200039357600080fd5b6000620003a184846200036d565b949350505050565b60006200037a82620003c3565b60006200037a82620003a9565b6001600160a01b031690565b620003da81620003b6565b8114620003e657600080fd5b50565b61666d80620003f96000396000f3fe6080604052600436106103355760003560e01c806372b6f1bf116101ab578063c762a46c116100f7578063dc1536b211610095578063f4b9fa751161006f578063f4b9fa75146107c4578063f56e281f146107d9578063f69e2046146107ee578063fbe4ed951461080357610335565b8063dc1536b21461076f578063e2a7515e14610784578063f484966b1461079757610335565b8063c9b42c67116100d1578063c9b42c671461071b578063cede5f6a14610730578063d393c3e914610745578063d77366a41461075a57610335565b8063c762a46c146106dc578063c77b9de6146106f1578063c92577751461070657610335565b80638bdb2afa11610164578063b0a7ef291161013e578063b0a7ef2914610688578063b3bc78441461069d578063b69d0456146106b2578063c11f4f11146106c757610335565b80638bdb2afa14610649578063a1b4d0111461065e578063a734f06e1461067357610335565b806372b6f1bf146105c057806375a8b012146105e057806375b5be2d146105f55780637a88bdbd1461060a578063819faf7b1461061f578063851954fa1461063457610335565b80633e413bee116102855780635aa8fb481161022357806364ec4e5c116101fd57806364ec4e5c1461056e5780636b5a4ca2146105835780636b9589aa146105985780636cbc4a6e146105ab57610335565b80635aa8fb481461052f5780635ae51b82146105445780635c0cb4791461055957610335565b806344211d621161025f57806344211d62146104db5780634a7101d5146104f05780634b57b0be1461050557806351f1985c1461051a57610335565b80633e413bee146104915780634037f967146104a6578063423d03f9146104c657610335565b806322320c98116102f25780632f48ab7d116102cc5780632f48ab7d1461043d57806334b4dabb14610452578063372a26cb146104675780633ca5b2341461047c57610335565b806322320c98146103fe5780632d3b5207146104135780632e707bd21461042857610335565b8063085e2c5b1461034457806312dea1601461037b5780631388b4201461039d57806313989140146103b25780632113240d146103d457806321a360f5146103e9575b3332141561034257600080fd5b005b34801561035057600080fd5b5061036461035f366004615b93565b610818565b6040516103729291906164b9565b60405180910390f35b34801561038757600080fd5b506103906108b8565b604051610372919061620f565b3480156103a957600080fd5b506103906108c7565b3480156103be57600080fd5b506103c76108d6565b60405161037291906164ab565b3480156103e057600080fd5b506103c76108dc565b3480156103f557600080fd5b506103c76108e2565b34801561040a57600080fd5b506103906108eb565b34801561041f57600080fd5b506103c76108fa565b34801561043457600080fd5b506103c7610903565b34801561044957600080fd5b50610390610908565b34801561045e57600080fd5b506103c7610917565b34801561047357600080fd5b5061039061091c565b34801561048857600080fd5b5061039061092b565b34801561049d57600080fd5b5061039061093a565b3480156104b257600080fd5b506103906104c1366004615ab8565b610949565b3480156104d257600080fd5b50610390610c32565b3480156104e757600080fd5b506103c7610c41565b3480156104fc57600080fd5b506103c7610c46565b34801561051157600080fd5b50610390610c4b565b34801561052657600080fd5b50610390610c5a565b34801561053b57600080fd5b506103c7610c69565b34801561055057600080fd5b506103c7610c6f565b34801561056557600080fd5b506103c7610c75565b34801561057a57600080fd5b506103c7610c7a565b34801561058f57600080fd5b50610390610c81565b6103426105a6366004615c08565b610c90565b3480156105b757600080fd5b506103c7610cb9565b3480156105cc57600080fd5b506103906105db366004615ab8565b610cc0565b3480156105ec57600080fd5b506103c7610eaa565b34801561060157600080fd5b50610390610eb0565b34801561061657600080fd5b506103c7610ebf565b34801561062b57600080fd5b50610390610ec4565b34801561064057600080fd5b50610390610ed3565b34801561065557600080fd5b50610390610ee2565b34801561066a57600080fd5b50610390610ef1565b34801561067f57600080fd5b50610390610f00565b34801561069457600080fd5b506103c7610f18565b3480156106a957600080fd5b506103c7610f1e565b3480156106be57600080fd5b50610390610f27565b3480156106d357600080fd5b50610390610f36565b3480156106e857600080fd5b506103c7610f45565b3480156106fd57600080fd5b506103c7610f4a565b34801561071257600080fd5b50610390610f50565b34801561072757600080fd5b506103c7610f5f565b34801561073c57600080fd5b50610390610f66565b34801561075157600080fd5b506103c7610f75565b34801561076657600080fd5b50610390610f7c565b34801561077b57600080fd5b506103c7610f8b565b610342610792366004615af4565b610f91565b3480156107a357600080fd5b506107b76107b2366004615b93565b611058565b60405161037291906161fe565b3480156107d057600080fd5b506103906110ec565b3480156107e557600080fd5b506103c76110fb565b3480156107fa57600080fd5b50610390611100565b34801561080f57600080fd5b5061039061110f565b601a5460405163085e2c5b60e01b81526000916060916001600160a01b039091169063085e2c5b90610856908a908a908a908a908a906004016162c7565b60006040518083038186803b15801561086e57600080fd5b505afa158015610882573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526108aa9190810190615ccb565b915091509550959350505050565b600a546001600160a01b031681565b600c546001600160a01b031681565b61200081565b61800081565b64020000000081565b6011546001600160a01b031681565b64010000000081565b608081565b6002546001600160a01b031681565b604081565b6010546001600160a01b031681565b6004546001600160a01b031681565b6001546001600160a01b031681565b600061095d826001600160a01b031661111e565b1561096b5750600019610c2d565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b17905290516000916060916001600160a01b03861691611388916109b4919061612f565b6000604051808303818686fa925050503d80600081146109f0576040519150601f19603f3d011682016040523d82523d6000602084013e6109f5565b606091505b509150915081610a0b5760001992505050610c2d565b6000805b6007835103811015610b6457828160000181518110610a2a57fe5b6020910101516001600160f81b031916602360f91b148015610a6d5750828160010181518110610a5657fe5b6020910101516001600160f81b031916607560f81b145b8015610a9a5750828160020181518110610a8357fe5b6020910101516001600160f81b031916601b60fa1b145b8015610ac75750828160030181518110610ab057fe5b6020910101516001600160f81b031916606360f81b145b8015610af45750828160040181518110610add57fe5b6020910101516001600160f81b031916603960f91b145b8015610b215750828160050181518110610b0a57fe5b6020910101516001600160f81b031916607560f81b145b8015610b4e5750828160060181518110610b3757fe5b6020910101516001600160f81b031916606d60f81b145b15610b5c5760019150610b64565b600101610a0f565b5080610b77576000199350505050610c2d565b60408051600481526024810182526020810180516001600160e01b031663797bf38560e01b17905290516001600160a01b0387169161138891610bba919061612f565b6000604051808303818686fa925050503d8060008114610bf6576040519150601f19603f3d011682016040523d82523d6000602084013e610bfb565b606091505b50909350915082610c13576000199350505050610c2d565b81806020019051610c279190810190615ad6565b93505050505b919050565b600f546001600160a01b031681565b601081565b602081565b6006546001600160a01b031681565b600d546001600160a01b031681565b61400081565b61080081565b600881565b6202000081565b6014546001600160a01b031681565b6060610c9f8787878686610818565b915050610cb0878787878587610f91565b50505050505050565b6208000081565b6000610cd4826001600160a01b031661111e565b15610ce25750600019610c2d565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b17905290516000916060916001600160a01b0386169161138891610d2b919061612f565b6000604051808303818686fa925050503d8060008114610d67576040519150601f19603f3d011682016040523d82523d6000602084013e610d6c565b606091505b509150915081610d825760001992505050610c2d565b6000805b6004835103811015610e5457828160000181518110610da157fe5b6020910101516001600160f81b031916604160f81b148015610de45750828160010181518110610dcd57fe5b6020910101516001600160f81b031916606160f81b145b8015610e115750828160020181518110610dfa57fe5b6020910101516001600160f81b031916603b60f91b145b8015610e3e5750828160030181518110610e2757fe5b6020910101516001600160f81b031916606560f81b145b15610e4c5760019150610e54565b600101610d86565b5080610e67576000199350505050610c2d565b60408051600481526024810182526020810180516001600160e01b0316632274683f60e21b17905290516001600160a01b0387169161138891610bba919061612f565b61040081565b6003546001600160a01b031681565b600281565b6015546001600160a01b031681565b6007546001600160a01b031681565b6009546001600160a01b031681565b6017546001600160a01b031681565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b6012546001600160a01b031681565b6013546001600160a01b031681565b600181565b61020081565b6005546001600160a01b031681565b6204000081565b600e546001600160a01b031681565b6201000081565b6008546001600160a01b031681565b61010081565b610fac6001600160a01b03871633308763ffffffff61115816565b610fb98686868585611258565b6000610fd46001600160a01b0387163063ffffffff61128b16565b905083811015610fff5760405162461bcd60e51b8152600401610ff69061641e565b60405180910390fd5b6110196001600160a01b038716338363ffffffff61132f16565b5061104e336110376001600160a01b038a163063ffffffff61128b16565b6001600160a01b038a16919063ffffffff61132f16565b5050505050505050565b606082604051908082528060200260200182016040528015611084578160200160208202803883390190505b50905060005b838110156110e2576110c287876110ba876110ae8a6001880163ffffffff6113ac16565b9063ffffffff6113e616565b600187610818565b508282815181106110cf57fe5b602090810291909101015260010161108a565b5095945050505050565b6000546001600160a01b031681565b600481565b6016546001600160a01b031681565b601a546001600160a01b031681565b60006001600160a01b038216158061115257506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b92915050565b8061116257611252565b61116b8461111e565b15611237576001600160a01b038316331480156111885750803410155b6111a45760405162461bcd60e51b8152600401610ff69061644e565b6001600160a01b03821630146111ec576040516001600160a01b0383169082156108fc029083906000818181858888f193505050501580156111ea573d6000803e3d6000fd5b505b8034111561123257336108fc611208348463ffffffff61142816565b6040518115909202916000818181858888f19350505050158015611230573d6000803e3d6000fd5b505b611252565b6112526001600160a01b03851684848463ffffffff61146a16565b50505050565b836001600160a01b0316856001600160a01b0316141561127757611284565b61128485858585856114c5565b5050505050565b60006112968361111e565b156112ac57506001600160a01b03811631611152565b6040516370a0823160e01b81526001600160a01b038416906370a08231906112d890859060040161613b565b60206040518083038186803b1580156112f057600080fd5b505afa158015611304573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506113289190810190615cad565b9392505050565b60008161133e57506001611328565b6113478461111e565b15611388576040516001600160a01b0384169083156108fc029084906000818181858888f19350505050158015611382573d6000803e3d6000fd5b50611328565b6113a26001600160a01b038516848463ffffffff61163016565b5060019392505050565b6000826113bb57506000611152565b828202828482816113c857fe5b04146113285760405162461bcd60e51b8152600401610ff69061645e565b600061132883836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250611657565b600061132883836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061168e565b6040516112529085906323b872dd60e01b9061148e9087908790879060240161619b565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526116ba565b836001600160a01b0316856001600160a01b031614156114e457611284565b6114f68161010063ffffffff61179f16565b611623576018546040516302091f7b60e51b81526001600160a01b0390911690634123ef609061152a90889060040161620f565b60206040518083038186803b15801561154257600080fd5b505afa158015611556573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061157a9190810190615a60565b156115915761158c85858585856117a5565b611284565b6018546040516302091f7b60e51b81526001600160a01b0390911690634123ef60906115c190879060040161620f565b60206040518083038186803b1580156115d957600080fd5b505afa1580156115ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116119190810190615a60565b156116235761158c8585858585611b33565b6112848585858585612136565b60405161165290849063a9059cbb60e01b9061148e90869086906024016161c3565b505050565b600081836116785760405162461bcd60e51b8152600401610ff691906163fd565b50600083858161168457fe5b0495945050505050565b600081848411156116b25760405162461bcd60e51b8152600401610ff691906163fd565b505050900390565b6116cc826001600160a01b0316612143565b6116e85760405162461bcd60e51b8152600401610ff69061649b565b60006060836001600160a01b031683604051611704919061612f565b6000604051808303816000865af19150503d8060008114611741576040519150601f19603f3d011682016040523d82523d6000602084013e611746565b606091505b5091509150816117685760405162461bcd60e51b8152600401610ff69061642e565b80511561125257808060200190516117839190810190615a60565b6112525760405162461bcd60e51b8152600401610ff69061646e565b16151590565b6117ad615748565b6117b68661217f565b905060608160000151516040519080825280602002602001820160405280156117e9578160200160208202803883390190505b509050606082600001515160405190808252806020026020018201604052801561182757816020015b60608152602001906001900390816118125790505b50905060005b83515161ffff821610156119a4578551604051908082528060200260200182016040528015611866578160200160208202803883390190505b50828261ffff168151811061187757fe5b602002602001018190525083600001518161ffff168151811061189657fe5b6020026020010151600001516001600160a01b03166370a08231336040518263ffffffff1660e01b81526004016118cd9190616149565b60206040518083038186803b1580156118e557600080fd5b505afa1580156118f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061191d9190810190615cad565b838261ffff168151811061192d57fe5b602090810291909101015260005b865181101561199b578160080261ffff1687828151811061195857fe5b6020026020010151901c60ff16838361ffff168151811061197557fe5b6020026020010151828151811061198857fe5b602090810291909101015260010161193b565b5060010161182d565b5082602001516001600160a01b031663415f1240876040518263ffffffff1660e01b81526004016119d591906164ab565b600060405180830381600087803b1580156119ef57600080fd5b505af1158015611a03573d6000803e3d6000fd5b505084515160009250159050611b2857600084600001518261ffff1681518110611a2957fe5b6020026020010151600001516001600160a01b03166370a08231336040518263ffffffff1660e01b8152600401611a609190616149565b60206040518083038186803b158015611a7857600080fd5b505afa158015611a8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ab09190810190615cad565b9050611b1e85600001518361ffff1681518110611ac957fe5b6020026020010151600001518a611b00878661ffff1681518110611ae957fe5b60200260200101518561142890919063ffffffff16565b868661ffff1681518110611b1057fe5b60200260200101518a612136565b5050505050611284565b505050505050505050565b600019611b3e615748565b611b478661217f565b90506060816000015151604051908082528060200260200182016040528015611b7a578160200160208202803883390190505b50905060005b82515161ffff82161015611f37576000611bbe8885600001518461ffff1681518110611ba857fe5b6020026020010151602001518660400151612228565b9050600084600001518361ffff1681518110611bd657fe5b6020026020010151600001516001600160a01b03166370a08231336040518263ffffffff1660e01b8152600401611c0d9190616149565b60206040518083038186803b158015611c2557600080fd5b505afa158015611c39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611c5d9190810190615cad565b905060005b8851811015611cab578360080261ffff16898281518110611c7f57fe5b6020026020010151901c60ff16898281518110611c9857fe5b6020908102919091010152600101611c62565b50611cd88b86600001518561ffff1681518110611cc457fe5b602002602001015160000151848b8b612136565b600085600001518461ffff1681518110611cee57fe5b6020026020010151600001516001600160a01b03166370a08231336040518263ffffffff1660e01b8152600401611d259190616149565b60206040518083038186803b158015611d3d57600080fd5b505afa158015611d51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d759190810190615cad565b9050611ea986600001518561ffff1681518110611d8e57fe5b6020026020010151600001516001600160a01b03166370a0823188602001516040518263ffffffff1660e01b8152600401611dc9919061613b565b60206040518083038186803b158015611de157600080fd5b505afa158015611df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e199190810190615cad565b6110ae611e2c848663ffffffff61142816565b8e6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611e6557600080fd5b505afa158015611e79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e9d9190810190615cad565b9063ffffffff6113ac16565b858561ffff1681518110611eb957fe5b60200260200101818152505086858561ffff1681518110611ed657fe5b60200260200101511015611eff57848461ffff1681518110611ef457fe5b602002602001015196505b611f2c86600001518561ffff1681518110611f1657fe5b602002602001015160000151876020015161223e565b505050600101611b80565b5081602001516001600160a01b031663ca1d209d846040518263ffffffff1660e01b8152600401611f6891906164ab565b600060405180830381600087803b158015611f8257600080fd5b505af1158015611f96573d6000803e3d6000fd5b506000925050505b82515161ffff82161015611b2857600083600001518261ffff1681518110611fc257fe5b6020026020010151600001516001600160a01b03166370a0823185602001516040518263ffffffff1660e01b8152600401611ffd919061613b565b60206040518083038186803b15801561201557600080fd5b505afa158015612029573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061204d9190810190615cad565b905060006120f38a6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561208d57600080fd5b505afa1580156120a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506120c59190810190615cad565b6110ae84611e9d8a898961ffff16815181106120dd57fe5b602002602001015161142890919063ffffffff16565b9050801561212c5761212a856020015186600001518561ffff168151811061211757fe5b6020026020010151600001518c846122fb565b505b5050600101611f9e565b6112848585858585612497565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061217757508115155b949350505050565b612187615748565b6000826001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156121c257600080fd5b505afa1580156121d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506121fa9190810190615ad6565b90506060600061220983612757565b9085526001600160a01b03909316602085015250506040820152919050565b6000612177826110ae868663ffffffff6113ac16565b612250826001600160a01b031661111e565b6122f757604051636eb1769f60e11b815260ff906001600160a01b0384169063dd62ed3e906122859030908690600401616157565b60206040518083038186803b15801561229d57600080fd5b505afa1580156122b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506122d59190810190615cad565b901c6122f7576122f76001600160a01b0383168260001963ffffffff61293716565b5050565b60006010856001600160a01b03166354fd4d506040518163ffffffff1660e01b815260040160206040518083038186803b15801561233857600080fd5b505afa15801561234c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506123709190810190615c8f565b61ffff1610612409576040516301bafcdb60e61b81526001600160a01b03861690636ebf36c0906123b0908790879087906000908190819060040161626d565b602060405180830381600087803b1580156123ca57600080fd5b505af11580156123de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506124029190810190615cad565b9050612177565b6040516375892cf160e01b81526001600160a01b038616906375892cf19061243c90879087908790600090600401616238565b602060405180830381600087803b15801561245657600080fd5b505af115801561246a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061248e9190810190615cad565b95945050505050565b836001600160a01b0316856001600160a01b031614156124b657611284565b6124c9816208000063ffffffff61179f16565b61274a576006546001600160a01b03868116911614156125da576006546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a082319061251b903090600401616149565b60206040518083038186803b15801561253357600080fd5b505afa158015612547573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061256b9190810190615cad565b6040518263ffffffff1660e01b815260040161258791906164ab565b600060405180830381600087803b1580156125a157600080fd5b505af11580156125b5573d6000803e3d6000fd5b5050505061158c73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee85858585612a0c565b6007546001600160a01b0386811691161415612628576007546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a082319061251b903090600401616149565b6006546001600160a01b03858116911614156126c35761265f8573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585612497565b600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b1580156126af57600080fd5b505af1158015611b1e573d6000803e3d6000fd5b6007546001600160a01b038581169116141561274a576126fa8573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee858585612497565b600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b1580156126af57600080fd5b6112848585858585612a0c565b60606000826001600160a01b03166371f52bf36040518163ffffffff1660e01b815260040160206040518083038186803b15801561279457600080fd5b505afa1580156127a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506127cc9190810190615cad565b60405190808252806020026020018201604052801561280557816020015b6127f2615772565b8152602001906001900390816127ea5790505b50915060005b8251811015612931576040516319b6401560e01b81526001600160a01b038516906319b64015906128409084906004016164ab565b60206040518083038186803b15801561285857600080fd5b505afa15801561286c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506128909190810190615ad6565b83828151811061289c57fe5b6020026020010151600001906001600160a01b031690816001600160a01b0316815250506128e1848483815181106128d057fe5b602002602001015160000151612a19565b8382815181106128ed57fe5b6020026020010151602001818152505061292783828151811061290c57fe5b60200260200101516020015183612b5090919063ffffffff16565b915060010161280b565b50915091565b6129408361111e565b611652576000811180156129d25750604051636eb1769f60e11b81526000906001600160a01b0385169063dd62ed3e90612980903090879060040161618d565b60206040518083038186803b15801561299857600080fd5b505afa1580156129ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506129d09190810190615cad565b115b156129f2576129f26001600160a01b03841683600063ffffffff612b7516565b6116526001600160a01b038416838363ffffffff612b7516565b6112848585858585612c3b565b60006016836001600160a01b03166354fd4d506040518163ffffffff1660e01b815260040160206040518083038186803b158015612a5657600080fd5b505afa158015612a6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612a8e9190810190615c8f565b61ffff1610612ac3576040516316095a2360e11b81526001600160a01b03841690632c12b446906112d890859060040161620f565b60405163359af54960e21b81526001600160a01b0384169063d66bd52490612aef90859060040161613b565b60a06040518083038186803b158015612b0757600080fd5b505afa158015612b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b3f9190810190615c71565b6020015163ffffffff169392505050565b6000828201838110156113285760405162461bcd60e51b8152600401610ff69061640e565b801580612bfd5750604051636eb1769f60e11b81526001600160a01b0384169063dd62ed3e90612bab903090869060040161618d565b60206040518083038186803b158015612bc357600080fd5b505afa158015612bd7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612bfb9190810190615cad565b155b612c195760405162461bcd60e51b8152600401610ff69061648b565b60405161165290849063095ea7b360e01b9061148e90869086906024016161c3565b836001600160a01b0316856001600160a01b03161415612c5a57611284565b612c62615789565b612c6a613036565b9050612c7e8261080063ffffffff61179f16565b6130215760005b600a811015612e43578181600a8110612c9a57fe5b60200201516001600160a01b0316876001600160a01b03161415612e3b5760008282600a8110612cc657fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015612d0357600080fd5b505afa158015612d17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612d3b9190810190615ad6565b90508282600a8110612d4957fe5b60200201516001600160a01b0316632e1a7d4d876040518263ffffffff1660e01b8152600401612d7991906164ab565b600060405180830381600087803b158015612d9357600080fd5b505af1158015612da7573d6000803e3d6000fd5b50505050612e338188836001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401612ddc9190616149565b60206040518083038186803b158015612df457600080fd5b505afa158015612e08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e2c9190810190615cad565b8888612c3b565b505050611284565b600101612c85565b5060005b600a81101561301f578181600a8110612e5c57fe5b60200201516001600160a01b0316866001600160a01b031614156130175760008282600a8110612e8857fe5b60200201516001600160a01b031663fc0c546a6040518163ffffffff1660e01b815260040160206040518083038186803b158015612ec557600080fd5b505afa158015612ed9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612efd9190810190615ad6565b9050612f0c8882888888613152565b612f26818484600a8110612f1c57fe5b602002015161223e565b8282600a8110612f3257fe5b60200201516001600160a01b031663b6b55f25826001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401612f719190616149565b60206040518083038186803b158015612f8957600080fd5b505afa158015612f9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612fc19190810190615cad565b6040518263ffffffff1660e01b8152600401612fdd91906164ab565b600060405180830381600087803b158015612ff757600080fd5b505af115801561300b573d6000803e3d6000fd5b50505050505050611284565b600101612e47565b505b61302e8686868686613152565b505050505050565b61303e615789565b5060408051610140810182527316de59092dae5ccf4a1e6439d611fd0653f0bd0181527304aa51bbcb46541455ccf1b8bef2ebc5d3787ec960208201527373a052500105205d34daf004eab301916da8190f918101919091527383f798e925bcd4017eb265844fddabb448f1707d606082015273d6ad7a6750a7593e092a9b218d66c0a814a3436e608082015273f61718057901f84c4eec4339ef8f0d86d2b4560060a08201527304bc0ab673d88ae9dbc9da2380cb6b79c4bca9ae60c082015273c2cb1040220768554cf699b0d863a3cd4324ce3260e082015273e6354ed5bc4b393a5aad09f21c46e101e692d4476101008201527326ea744e5b887e5205727f55dfbe8685e3b2195161012082015290565b61128485858585855b836001600160a01b0316856001600160a01b0316141561317a57611284565b61318b81601063ffffffff61179f16565b6133ad57613198856133ba565b1561325b5760006131a8866134b8565b60405163db006a7560e01b81529091506001600160a01b0387169063db006a75906131d79087906004016164ab565b602060405180830381600087803b1580156131f157600080fd5b505af1158015613205573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506132299190810190615cad565b5060006132456001600160a01b0383163063ffffffff61128b16565b9050613254828783878761315b565b5050611284565b613264846133ba565b156133ad576000613274856134b8565b9050613283868286868661358d565b600061329e6001600160a01b0383163063ffffffff61128b16565b90506132b2826001600160a01b031661111e565b1561332557601760009054906101000a90046001600160a01b03166001600160a01b0316631249c58b826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561330757600080fd5b505af115801561331b573d6000803e3d6000fd5b5050505050613254565b61332f828761223e565b60405163140e25ad60e31b81526001600160a01b0387169063a0712d689061335b9084906004016164ab565b602060405180830381600087803b15801561337557600080fd5b505af1158015613389573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612e339190810190615cad565b611284858585858561358d565b6017546000906001600160a01b03838116911614156133db57506001610c2d565b6016546040516000916060916001600160a01b039091169061138890638e8f294b60e01b9061340e90889060240161620f565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161344c919061612f565b6000604051808303818686fa925050503d8060008114613488576040519150601f19603f3d011682016040523d82523d6000602084013e61348d565b606091505b5091509150816134a257600092505050610c2d565b6000818060200190516110e29190810190615a7e565b6017546000906001600160a01b03838116911614156134d957506000610c2d565b60408051600481526024810182526020810180516001600160e01b0316636f307dc360e01b17905290516000916060916001600160a01b0386169161138891613522919061612f565b6000604051808303818686fa925050503d806000811461355e576040519150601f19603f3d011682016040523d82523d6000602084013e613563565b606091505b5091509150816135795760001992505050610c2d565b808060200190516121779190810190615ad6565b6112848585858585836001600160a01b0316856001600160a01b031614156135b457611284565b6135c581602063ffffffff61179f16565b61384b5760006135d486610949565b90506001600160a01b038082161461372f576135f8816001600160a01b031661111e565b156136835760405163081a6b2560e41b81526001600160a01b038716906381a6b2509061362b9030908890600401616172565b602060405180830381600087803b15801561364557600080fd5b505af1158015613659573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061367d9190810190615cad565b50613705565b604051632770a7eb60e21b81526001600160a01b03871690639dc29fac906136b19030908890600401616172565b602060405180830381600087803b1580156136cb57600080fd5b505af11580156136df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506137039190810190615cad565b505b60006137206001600160a01b0383163063ffffffff61128b16565b90506132548287838787613854565b61373885610949565b90506001600160a01b0380821614613849576137578682868686613854565b60006137726001600160a01b0383163063ffffffff61128b16565b9050613786826001600160a01b031661111e565b1561381157604051638f6ede1f60e01b81526001600160a01b03871690638f6ede1f9083906137b9903090600401616149565b6020604051808303818588803b1580156137d257600080fd5b505af11580156137e6573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525061380b9190810190615cad565b50613254565b61381b828761223e565b6040516340c10f1960e01b81526001600160a01b038716906340c10f199061335b9030908590600401616172565b505b61128485858585855b61128485858585855b836001600160a01b0316856001600160a01b0316141561387c57611284565b61388d81608063ffffffff61179f16565b613a8f57600061389c86610cc0565b90506001600160a01b038082161461391f5760405163db006a7560e01b81526001600160a01b0387169063db006a75906138da9087906004016164ab565b600060405180830381600087803b1580156138f457600080fd5b505af1158015613908573d6000803e3d6000fd5b50505050613919818686868661385d565b50611284565b61392885610cc0565b90506001600160a01b0380821614613a8d576139478682868686613a98565b60006139626001600160a01b0383163063ffffffff61128b16565b90506139f382601560009054906101000a90046001600160a01b03166001600160a01b031663f2f4eb266040518163ffffffff1660e01b815260040160206040518083038186803b1580156139b657600080fd5b505afa1580156139ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506139ee9190810190615a0d565b61223e565b6015546001600160a01b039081169063d2d0e06690613a1390851661111e565b613a1e576000613a20565b825b613a32856001600160a01b031661111e565b613a3c5784613a52565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b8461044d6040518563ffffffff1660e01b8152600401613a74939291906163ac565b6000604051808303818588803b158015612ff757600080fd5b505b61128485858585855b836001600160a01b0316856001600160a01b03161415613ab757611284565b613ac98161040063ffffffff61179f16565b613d4f576013546001600160a01b0386811691161415613c2557601354604051637f8661a160e01b81526001600160a01b0390911690637f8661a190613b139086906004016164ab565b600060405180830381600087803b158015613b2d57600080fd5b505af1158015613b41573d6000803e3d6000fd5b50506014546040516370a0823160e01b8152600093506001600160a01b0390911691506370a0823190613b78903090600401616149565b60206040518083038186803b158015613b9057600080fd5b505afa158015613ba4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613bc89190810190615cad565b90508015613c0c57601454606090613bed906001600160a01b03168784600187610818565b601454909250613c0a91506001600160a01b031687848487611258565b505b600054613919906001600160a01b031686868686613d58565b6013546001600160a01b0385811691161415613d4f57600054613c559086906001600160a01b0316858585613d58565b600054601354613c71916001600160a01b03908116911661223e565b6013546000546040516370a0823160e01b81526001600160a01b039283169263049878f39216906370a0823190613cac903090600401616149565b60206040518083038186803b158015613cc457600080fd5b505afa158015613cd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613cfc9190810190615cad565b6040518263ffffffff1660e01b8152600401613d1891906164ab565b600060405180830381600087803b158015613d3257600080fd5b505af1158015613d46573d6000803e3d6000fd5b50505050611284565b61128485858585855b836001600160a01b0316856001600160a01b03161415613d7757611284565b613d8881604063ffffffff61179f16565b613f8c576012546001600160a01b0386811691161415613e955760125460405163ef693bed60e01b81526001600160a01b039091169063ef693bed90613dd49030908790600401616172565b600060405180830381600087803b158015613dee57600080fd5b505af1158015613e02573d6000803e3d6000fd5b50506000546040516370a0823160e01b815261158c93506001600160a01b039091169150869082906370a0823190613e3e903090600401616149565b60206040518083038186803b158015613e5657600080fd5b505afa158015613e6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613e8e9190810190615cad565b8585613f95565b6012546001600160a01b0385811691161415613f8c57600054613ec59086906001600160a01b0316858585613f95565b600054601254613ee1916001600160a01b03908116911661223e565b6012546000546040516370a0823160e01b81526001600160a01b0392831692633b4da69f9230929116906370a0823190613f1f908490600401616149565b60206040518083038186803b158015613f3757600080fd5b505afa158015613f4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613f6f9190810190615cad565b6040518363ffffffff1660e01b8152600401613d18929190616172565b61128485858585855b613fa7856001600160a01b031661111e565b158015613fc35750613fc1846001600160a01b031661111e565b155b8015613fdc5750613fdc8161020063ffffffff61179f16565b156140dd576060825160405190808252806020026020018201604052801561400e578160200160208202803883390190505b50905060005b83518110156140535783818151811061402957fe5b602002602001015160ff1682828151811061404057fe5b6020908102919091010152600101614014565b506140758673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8684866143e7565b60005b83518110156140bb57600884828151811061408f57fe5b6020026020010151901c60ff168282815181106140a857fe5b6020908102919091010152600101614078565b5061391973eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee864784866143e7565b6000546001600160a01b0386811691161480159061410957506000546001600160a01b03858116911614155b80156141235750614123816201000063ffffffff61179f16565b156142895760608251604051908082528060200260200182016040528015614155578160200160208202803883390190505b50905060005b835181101561419a5783818151811061417057fe5b602002602001015160ff1682828151811061418757fe5b602090810291909101015260010161415b565b506000546141b59087906001600160a01b03168684866143e7565b60005b83518110156141fb5760088482815181106141cf57fe5b6020026020010151901c60ff168282815181106141e857fe5b60209081029190910101526001016141b8565b506000546040516370a0823160e01b8152613919916001600160a01b031690879082906370a0823190614232903090600401616149565b60206040518083038186803b15801561424a57600080fd5b505afa15801561425e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506142829190810190615cad565b84866143e7565b6001546001600160a01b038681169116148015906142b557506001546001600160a01b03858116911614155b80156142cf57506142cf816202000063ffffffff61179f16565b156143de5760608251604051908082528060200260200182016040528015614301578160200160208202803883390190505b50905060005b83518110156143465783818151811061431c57fe5b602002602001015160ff1682828151811061433357fe5b6020908102919091010152600101614307565b506001546143619087906001600160a01b03168684866143e7565b60005b83518110156143a757600884828151811061437b57fe5b6020026020010151901c60ff1682828151811061439457fe5b6020908102919091010152600101614364565b506001546040516370a0823160e01b8152613919916001600160a01b031690879082906370a0823190614232903090600401616149565b61128485858585855b836001600160a01b0316856001600160a01b0316141561440657611284565b61440e6157a8565b50604080516101208101825261458381526147f7602082015261493491810191909152614cc16060820152614f50608082015261509760a08201526151e660c082015261538860e082015261552a610100820152600080805b60098110156144c057600086828151811061447e57fe5b602002602001015111156144b8576144b286828151811061449b57fe5b602002602001015184612b5090919063ffffffff16565b92508091505b600101614467565b50600082116144e15760405162461bcd60e51b8152600401610ff69061643e565b8560005b6009811015614577578681815181106144fa57fe5b60200260200101516000141561450f5761456f565b600061453b856110ae8a858151811061452457fe5b60200260200101518c6113ac90919063ffffffff16565b9050838214156145485750815b808303925061456c8b8b8389866009811061455f57fe5b602002015163ffffffff16565b50505b6001016144e5565b50505050505050505050565b6000816145986001600160a01b03861661111e565b6146c0576009546040516303795fb160e11b81526000916001600160a01b0316906306f2bf62906145cd90899060040161620f565b60206040518083038186803b1580156145e557600080fd5b505afa1580156145f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061461d9190810190615ad6565b90506001600160a01b038116156146be57614638868261223e565b6040516395e3c50b60e01b81526001600160a01b038216906395e3c50b9061466990859060019042906004016164d9565b602060405180830381600087803b15801561468357600080fd5b505af1158015614697573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506146bb9190810190615cad565b91505b505b6146d2846001600160a01b031661111e565b612177576009546040516303795fb160e11b81526000916001600160a01b0316906306f2bf629061470790889060040161620f565b60206040518083038186803b15801561471f57600080fd5b505afa158015614733573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506147579190810190615ad6565b90506001600160a01b038116156147ee5760405163f39b5b9b60e01b81526001600160a01b0382169063f39b5b9b9084906147999060019042906004016163ef565b6020604051808303818588803b1580156147b257600080fd5b505af11580156147c6573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506147eb9190810190615cad565b91505b50949350505050565b6008546000906148119085906001600160a01b031661223e565b6008546001600160a01b03908116906329589f619061483190871661111e565b61483c57600061483e565b835b614850876001600160a01b031661111e565b61485a5786614870565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b85614883886001600160a01b031661111e565b61488d57876148a3565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b30600160ff1b6000734d37f28d2db99e8d35a6c725a5f1749a085850a36040518963ffffffff1660e01b81526004016148e29796959493929190616309565b6020604051808303818588803b1580156148fb57600080fd5b505af115801561490f573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506121779190810190615cad565b6000614948846001600160a01b031661111e565b156149b757600760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561499d57600080fd5b505af11580156149b1573d6000803e3d6000fd5b50505050505b600a54604051632ecd14d360e21b81526000916001600160a01b03169063bb34534c906149e69060040161647e565b60206040518083038186803b1580156149fe57600080fd5b505afa158015614a12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614a369190810190615a0d565b600b549091506060906001600160a01b03908116906375e1cc8290614a5c90891661111e565b614a665787614a73565b6007546001600160a01b03165b614a85886001600160a01b031661111e565b614a8f5787614a9c565b6007546001600160a01b03165b6040518363ffffffff1660e01b8152600401614ab992919061621d565b60006040518083038186803b158015614ad157600080fd5b505afa158015614ae5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052614b0d9190810190615a2b565b9050614b41614b24876001600160a01b031661111e565b614b2e5786614b3b565b6007546001600160a01b03165b8361223e565b6040516331ee892f60e21b81526000906001600160a01b0384169063c7ba24bc90614b7590859089906001906004016161d1565b602060405180830381600087803b158015614b8f57600080fd5b505af1158015614ba3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614bc79190810190615cad565b9050614bdb866001600160a01b031661111e565b15614cb7576007546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a0823190614c18903090600401616149565b60206040518083038186803b158015614c3057600080fd5b505afa158015614c44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614c689190810190615cad565b6040518263ffffffff1660e01b8152600401614c8491906164ab565b600060405180830381600087803b158015614c9e57600080fd5b505af1158015614cb2573d6000803e3d6000fd5b505050505b9695505050505050565b6000614cd5846001600160a01b031661111e565b15614d4457600660009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015614d2a57600080fd5b505af1158015614d3e573d6000803e3d6000fd5b50505050505b614d81614d59856001600160a01b031661111e565b614d635784614d70565b6006546001600160a01b03165b600c546001600160a01b031661223e565b600c546000906001600160a01b0390811690630621b4f690614da490881661111e565b614dae5786614dbb565b6006546001600160a01b03165b85614dce886001600160a01b031661111e565b614dd85787614de5565b6006546001600160a01b03165b60016040518563ffffffff1660e01b8152600401614e069493929190616384565b602060405180830381600087803b158015614e2057600080fd5b505af1158015614e34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614e589190810190615cad565b9050614e6c846001600160a01b031661111e565b15612177576006546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a0823190614ea9903090600401616149565b60206040518083038186803b158015614ec157600080fd5b505afa158015614ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250614ef99190810190615cad565b6040518263ffffffff1660e01b8152600401614f1591906164ab565b600060405180830381600087803b158015614f2f57600080fd5b505af1158015614f43573d6000803e3d6000fd5b5050505090509392505050565b60015460009081906001600160a01b03868116911614614f71576000614f74565b60025b6000546001600160a01b03878116911614614f90576000614f93565b60015b600154910160ff1691506000906001600160a01b0390811690861614614fba576000614fbd565b60025b6000546001600160a01b03878116911614614fd9576000614fdc565b60015b0160ff16905081600f0b60001480614ff7575080600f0b6000145b1561500757600092505050611328565b600d5461501e9087906001600160a01b031661223e565b600d54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b600060405180830381600087803b15801561507657600080fd5b505af115801561508a573d6000803e3d6000fd5b5050505050509392505050565b60025460009081906001600160a01b038681169116146150b85760006150bb565b60035b6001546001600160a01b038781169116146150d75760006150da565b60025b6000546001600160a01b038881169116146150f65760006150f9565b60015b60025491019190910160ff1691506000906001600160a01b0390811690861614615124576000615127565b60035b6001546001600160a01b03878116911614615143576000615146565b60025b6000546001600160a01b03888116911614615162576000615165565b60015b010160ff16905081600f0b60001480615181575080600f0b6000145b1561519157600092505050611328565b600e546151a89087906001600160a01b031661223e565b600e54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b60035460009081906001600160a01b0386811691161461520757600061520a565b60045b6002546001600160a01b03878116911614615226576000615229565b60035b6001546001600160a01b03888116911614615245576000615248565b60025b6000546001600160a01b03898116911614615264576000615267565b60015b01010160ff1690506000600360009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b0316146152a65760006152a9565b60045b6002546001600160a01b038781169116146152c55760006152c8565b60035b6001546001600160a01b038881169116146152e45760006152e7565b60025b6000546001600160a01b03898116911614615303576000615306565b60015b01010160ff16905081600f0b60001480615323575080600f0b6000145b1561533357600092505050611328565b600f5461534a9087906001600160a01b031661223e565b600f54604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b60045460009081906001600160a01b038681169116146153a95760006153ac565b60045b6002546001600160a01b038781169116146153c85760006153cb565b60035b6001546001600160a01b038881169116146153e75760006153ea565b60025b6000546001600160a01b03898116911614615406576000615409565b60015b01010160ff1690506000600460009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b03161461544857600061544b565b60045b6002546001600160a01b0387811691161461546757600061546a565b60035b6001546001600160a01b03888116911614615486576000615489565b60025b6000546001600160a01b038981169116146154a55760006154a8565b60015b01010160ff16905081600f0b600014806154c5575080600f0b6000145b156154d557600092505050611328565b6010546154ec9087906001600160a01b031661223e565b601054604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b60055460009081906001600160a01b0386811691161461554b57600061554e565b60055b6003546001600160a01b0387811691161461556a57600061556d565b60045b6002546001600160a01b0388811691161461558957600061558c565b60035b6001546001600160a01b038981169116146155a85760006155ab565b60025b6000546001600160a01b038a81169116146155c75760006155ca565b60015b0101010160ff1690506000600560009054906101000a90046001600160a01b03166001600160a01b0316856001600160a01b03161461560a57600061560d565b60055b6003546001600160a01b0387811691161461562957600061562c565b60045b6002546001600160a01b0388811691161461564857600061564b565b60035b6001546001600160a01b0389811691161461566757600061566a565b60025b6000546001600160a01b038a8116911614615686576000615689565b60015b0101010160ff16905081600f0b600014806156a7575080600f0b6000145b156156b757600092505050611328565b6005546001600160a01b038781169116148015906156e357506005546001600160a01b03868116911614155b156156f357600092505050611328565b60115461570a9087906001600160a01b031661223e565b601154604051635320bf6b60e11b81526001600160a01b039091169063a6417ed69061505c90600019808701919086019089906000906004016163d4565b60405180606001604052806060815260200160006001600160a01b03168152602001600081525090565b604080518082019091526000808252602082015290565b604051806101400160405280600a906020820280388339509192915050565b6040518061012001604052806009905b6157d18152602001906001900390816157b85790505090565bfe5b8051611152816165e6565b600082601f8301126157ef57600080fd5b81516158026157fd8261651b565b6164f4565b9150818183526020840193506020810190508385602084028201111561582757600080fd5b60005b83811015615853578161583d88826157d3565b845250602092830192919091019060010161582a565b5050505092915050565b600082601f83011261586e57600080fd5b813561587c6157fd8261651b565b915081818352602084019350602081019050838560208402820111156158a157600080fd5b60005b8381101561585357816158b788826159ec565b84525060209283019291909101906001016158a4565b600082601f8301126158de57600080fd5b81516158ec6157fd8261651b565b9150818183526020840193506020810190508385602084028201111561591157600080fd5b60005b83811015615853578161592788826159f7565b8452506020928301929190910190600101615914565b8051611152816165fd565b803561115281616606565b805161115281616606565b600060a0828403121561597057600080fd5b61597a60a06164f4565b9050600061598884846159f7565b825250602061599984848301615a02565b60208301525060406159ad8482850161593d565b60408301525060606159c18482850161593d565b60608301525060806159d58482850161593d565b60808301525092915050565b80516111528161660f565b803561115281616618565b805161115281616618565b805161115281616621565b600060208284031215615a1f57600080fd5b600061217784846157d3565b600060208284031215615a3d57600080fd5b815167ffffffffffffffff811115615a5457600080fd5b612177848285016157de565b600060208284031215615a7257600080fd5b6000612177848461593d565b60008060408385031215615a9157600080fd5b6000615a9d858561593d565b9250506020615aae858286016159f7565b9150509250929050565b600060208284031215615aca57600080fd5b60006121778484615948565b600060208284031215615ae857600080fd5b60006121778484615953565b60008060008060008060c08789031215615b0d57600080fd5b6000615b198989615948565b9650506020615b2a89828a01615948565b9550506040615b3b89828a016159ec565b9450506060615b4c89828a016159ec565b935050608087013567ffffffffffffffff811115615b6957600080fd5b615b7589828a0161585d565b92505060a0615b8689828a016159ec565b9150509295509295509295565b600080600080600060a08688031215615bab57600080fd5b6000615bb78888615948565b9550506020615bc888828901615948565b9450506040615bd9888289016159ec565b9350506060615bea888289016159ec565b9250506080615bfb888289016159ec565b9150509295509295909350565b60008060008060008060c08789031215615c2157600080fd5b6000615c2d8989615948565b9650506020615c3e89828a01615948565b9550506040615c4f89828a016159ec565b9450506060615c6089828a016159ec565b9350506080615b7589828a016159ec565b600060a08284031215615c8357600080fd5b6000612177848461595e565b600060208284031215615ca157600080fd5b600061217784846159e1565b600060208284031215615cbf57600080fd5b600061217784846159f7565b60008060408385031215615cde57600080fd5b6000615cea85856159f7565b925050602083015167ffffffffffffffff811115615d0757600080fd5b615aae858286016158cd565b6000615d1f8383615d42565b505060200190565b6000615d1f8383616126565b615d3c8161658f565b82525050565b615d3c8161654f565b6000615d5682616542565b615d608185616546565b9350615d6b8361653c565b8060005b83811015615d99578151615d838882615d13565b9750615d8e8361653c565b925050600101615d6f565b509495945050505050565b6000615daf82616542565b615db98185616546565b9350615dc48361653c565b8060005b83811015615d99578151615ddc8882615d27565b9750615de78361653c565b925050600101615dc8565b6000615dfd82616542565b615e078185610c2d565b9350615e178185602086016165b0565b9290920192915050565b615d3c8161655f565b615d3c8161656a565b615d3c8161659a565b615d3c816165a5565b6000615e5082616542565b615e5a8185616546565b9350615e6a8185602086016165b0565b615e73816165dc565b9093019392505050565b6000615e8a601b83616546565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b6000615ec3603583616546565b7f4f6e6553706c69743a2061637475616c2072657475726e20616d6f756e74206981527439903632b9b9903a3430b71036b4b72932ba3ab93760591b602082015260400192915050565b6000615f1a602083616546565b7f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815260200192915050565b6000615f53602f83616546565b7f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f81526e6e7461696e206e6f6e2d7a65726f7360881b602082015260400192915050565b6000615fa4602b83616546565b7f57726f6e6720757365616765206f66204554482e756e6976657273616c54726181526a6e7366657246726f6d282960a81b602082015260400192915050565b6000615ff1602183616546565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000611152600083616546565b6000616041602a83616546565b7f5361666545524332303a204552433230206f7065726174696f6e20646964206e8152691bdd081cdd58d8d9595960b21b602082015260400192915050565b6c42616e636f724e6574776f726b60981b9052565b60006160a2603683616546565b7f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f81527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b602082015260400192915050565b60006160fa601f83616546565b7f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400815260200192915050565b615d3c81616583565b60006113288284615df2565b602081016111528284615d42565b602081016111528284615d33565b604081016161658285615d33565b6113286020830184615d42565b604081016161808285615d33565b6113286020830184616126565b604081016161658285615d42565b606081016161a98286615d42565b6161b66020830185615d42565b6121776040830184616126565b604081016161808285615d42565b606080825281016161e28186615d4b565b90506161f16020830185616126565b6121776040830184615e33565b602080825281016113288184615da4565b602081016111528284615e21565b6040810161622b8285615e21565b6113286020830184615e21565b608081016162468287615e21565b6162536020830186615e21565b6162606040830185616126565b61248e6060830184615e33565b60c0810161627b8289615e21565b6162886020830188615e21565b6162956040830187616126565b6162a26060830186615e33565b6162af6080830185615d33565b6162bc60a0830184615e33565b979650505050505050565b60a081016162d58288615e21565b6162e26020830187615e21565b6162ef6040830186616126565b6162fc6060830185616126565b614cb76080830184616126565b6101008101616318828a615e21565b6163256020830189616126565b6163326040830188615e21565b61633f6060830187615d33565b61634c6080830186615e33565b61635960a0830185615e33565b61636660c0830184615d33565b81810360e083015261637781616027565b9998505050505050505050565b608081016163928287615e21565b61639f6020830186616126565b6162606040830185615e21565b606081016163ba8286615e21565b6163c76020830185616126565b6121776040830184615e3c565b608081016163e28287615e2a565b6162536020830186615e2a565b604081016161808285615e33565b602080825281016113288184615e45565b6020808252810161115281615e7d565b6020808252810161115281615eb6565b6020808252810161115281615f0d565b6020808252810161115281615f46565b6020808252810161115281615f97565b6020808252810161115281615fe4565b6020808252810161115281616034565b60208101610c2d82616080565b6020808252810161115281616095565b60208082528101611152816160ed565b602081016111528284616126565b604081016164c78285616126565b81810360208301526121778184615da4565b606081016164e78286616126565b6161b66020830185615e33565b60405181810167ffffffffffffffff8111828210171561651357600080fd5b604052919050565b600067ffffffffffffffff82111561653257600080fd5b5060209081020190565b60200190565b5190565b90815260200190565b600061115282616577565b151590565b60006111528261654f565b600f0b90565b61ffff1690565b6001600160a01b031690565b90565b63ffffffff1690565b60006111528261655f565b600061115282616583565b600061115282616570565b60005b838110156165cb5781810151838201526020016165b3565b838111156112525750506000910152565b601f01601f191690565b6165ef8161654f565b81146165fa57600080fd5b50565b6165ef8161655a565b6165ef8161655f565b6165ef81616570565b6165ef81616583565b6165ef8161658656fea365627a7a723158208b80610e5ccc7644892fc0a33e2a10b10d27b8f595dfa293f6f52c6a14cce8f66c6578706572696d656e74616cf564736f6c63430005110040 \ No newline at end of file From 34d6e0a41a9d1bc196f20e0a652f1f79f7305225 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Mon, 6 Apr 2020 00:12:11 +0300 Subject: [PATCH 30/60] Fix call --- contracts/OneSplitSmartToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index caf1097..a6433c8 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -476,7 +476,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { uint256 leftover = details.tokens[i].token.balanceOf(address(this)); - uint256 ret = this.swapOnBancorSafe( + uint256 ret = _swapOnBancorSafe( reserveToken, smartToken, leftover From 959fc5fd439acd288c08be7744723787aed15cb3 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Mon, 6 Apr 2020 00:52:14 +0300 Subject: [PATCH 31/60] Fix bug in view call --- contracts/OneSplitSmartToken.sol | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index a6433c8..b90e452 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -180,7 +180,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { for (uint i = 0; i < details.tokens.length; i++) { uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( smartToken.totalSupply(), - details.tokens[i].token.balanceOf(details.converter), + _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), uint32(details.totalRatio), amount ); @@ -253,7 +253,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { fundAmounts[i] = smartTokenFormula.calculatePurchaseReturn( smartToken.totalSupply(), - details.tokens[i].token.balanceOf(details.converter), + _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), uint32(details.totalRatio), tokenAmounts[i] ); @@ -275,7 +275,7 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { uint256 leftover = tokenAmounts[i].sub( smartTokenFormula.calculateLiquidateReturn( _smartToken.totalSupply().add(_minFundAmount), - details.tokens[i].token.balanceOf(details.converter).add(tokenAmounts[i]), + _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter).add(tokenAmounts[i]), uint32(details.totalRatio), _minFundAmount ) @@ -401,7 +401,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { this.swap( _canonicalSUSD(details.tokens[i].token), toToken, - details.tokens[i].token.balanceOf(address(this)), + _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)), 0, dist, disableFlags @@ -430,7 +430,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { if (details.tokens[i].token != fromToken) { - uint256 tokenBalanceBefore = details.tokens[i].token.balanceOf(address(this)); + uint256 tokenBalanceBefore = _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)); for (uint j = 0; j < distribution.length; j++) { dist[j] = (distribution[j] >> (i * 8)) & 0xFF; @@ -444,18 +444,18 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { disableFlags ); - uint256 tokenBalanceAfter = details.tokens[i].token.balanceOf(address(this)); + uint256 tokenBalanceAfter = _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)); curFundAmount = smartTokenFormula.calculatePurchaseReturn( smartToken.totalSupply(), - details.tokens[i].token.balanceOf(details.converter), + _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), uint32(details.totalRatio), tokenBalanceAfter.sub(tokenBalanceBefore) ); } else { curFundAmount = smartTokenFormula.calculatePurchaseReturn( smartToken.totalSupply(), - details.tokens[i].token.balanceOf(details.converter), + _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), uint32(details.totalRatio), exchangeAmount ); @@ -474,7 +474,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { for (uint i = 0; i < details.tokens.length; i++) { IERC20 reserveToken = _canonicalSUSD(details.tokens[i].token); - uint256 leftover = details.tokens[i].token.balanceOf(address(this)); + uint256 leftover = _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)); uint256 ret = _swapOnBancorSafe( reserveToken, From d58fb9fa2c0319926bd0ec7c2d720a4197ac9ec2 Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Mon, 6 Apr 2020 00:55:18 +0300 Subject: [PATCH 32/60] Fix --- contracts/OneSplitSmartToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index b90e452..ca0d884 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -465,7 +465,7 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { minFundAmount = curFundAmount; } - _infiniteApproveIfNeeded(details.tokens[i].token, details.converter); + _infiniteApproveIfNeeded(_canonicalSUSD(details.tokens[i].token), details.converter); } ISmartTokenConverter(details.converter).fund(minFundAmount); From 80743c247d616e17e148552778f3c3a9679b2ab4 Mon Sep 17 00:00:00 2001 From: Kirill Date: Sun, 12 Apr 2020 02:37:50 +0300 Subject: [PATCH 33/60] unworkable uniswap pool token contract --- contracts/IOneSplit.sol | 1 + contracts/OneSplit.sol | 7 +- contracts/OneSplitUniswapPoolToken.sol | 424 +++++++++++++++++++++++ contracts/interface/IUniswapExchange.sol | 4 + contracts/interface/IUniswapFactory.sol | 2 + 5 files changed, 436 insertions(+), 2 deletions(-) create mode 100644 contracts/OneSplitUniswapPoolToken.sol diff --git a/contracts/IOneSplit.sol b/contracts/IOneSplit.sol index 38c9d1a..c2d3fa9 100644 --- a/contracts/IOneSplit.sol +++ b/contracts/IOneSplit.sol @@ -32,6 +32,7 @@ contract IOneSplitConsts { uint256 public constant FLAG_ENABLE_UNISWAP_CHAI = 0x200000; // Works only when ETH<>DAI or FLAG_ENABLE_MULTI_PATH_ETH uint256 public constant FLAG_ENABLE_UNISWAP_AAVE = 0x400000; // Works only when one of assets is ETH or FLAG_ENABLE_MULTI_PATH_ETH uint256 public constant FLAG_DISABLE_IDLE = 0x800000; + uint256 public constant FLAG_DISABLE_UNISWAP_POOL_TOKEN = 0x1000000; } diff --git a/contracts/OneSplit.sol b/contracts/OneSplit.sol index 77bd31d..7633cac 100644 --- a/contracts/OneSplit.sol +++ b/contracts/OneSplit.sol @@ -11,6 +11,7 @@ import "./OneSplitIearn.sol"; import "./OneSplitIdle.sol"; import "./OneSplitAave.sol"; import "./OneSplitWeth.sol"; +import "./OneSplitUniswapPoolToken.sol"; //import "./OneSplitSmartToken.sol"; @@ -25,7 +26,8 @@ contract OneSplitView is OneSplitCompoundView, OneSplitIearnView, OneSplitIdleView(0x23E4D1536c449e4D79E5903B4A9ddc3655be8609), - OneSplitWethView + OneSplitWethView, + OneSplitUniswapPoolTokenView //OneSplitSmartTokenView { function() external { @@ -98,7 +100,8 @@ contract OneSplit is OneSplitCompound, OneSplitIearn, OneSplitIdle(0x23E4D1536c449e4D79E5903B4A9ddc3655be8609), - OneSplitWeth + OneSplitWeth, + OneSplitUniswapPoolToken //OneSplitSmartToken { IOneSplitView public oneSplitView; diff --git a/contracts/OneSplitUniswapPoolToken.sol b/contracts/OneSplitUniswapPoolToken.sol new file mode 100644 index 0000000..8a5bcf1 --- /dev/null +++ b/contracts/OneSplitUniswapPoolToken.sol @@ -0,0 +1,424 @@ +pragma solidity ^0.5.0; + +import "./interface/IUniswapExchange.sol"; +import "./interface/IUniswapFactory.sol"; +import "./OneSplitBase.sol"; + + +contract OneSplitUniswapPoolTokenBase { + using SafeMath for uint256; + + IUniswapFactory uniswapFactory = IUniswapFactory(0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95); + + function isLiquidityPool(IERC20 token) internal view returns (bool) { + return address(uniswapFactory.getToken(address(token))) != address(0); + } + +} + +contract OneSplitUniswapPoolTokenView is OneSplitBaseView, OneSplitUniswapPoolTokenBase { + + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + internal + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + if (fromToken == toToken) { + return (amount, new uint256[](DEXES_COUNT)); + } + + + if (!disableFlags.check(FLAG_DISABLE_UNISWAP_POOL_TOKEN)) { + bool isPoolTokenFrom = isLiquidityPool(fromToken); + bool isPoolTokenTo = isLiquidityPool(toToken); + + if (isPoolTokenFrom && isPoolTokenTo) { + ( + uint256 returnETHAmount, + uint256[] memory poolTokenFromDistribution + ) = _getExpectedReturnFromPoolToken( + fromToken, + ETH_ADDRESS, + amount, + parts, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + + ( + uint256 returnPoolTokenToAmount, + uint256[] memory poolTokenToDistribution + ) = _getExpectedReturnToPoolToken( + ETH_ADDRESS, + toToken, + returnETHAmount, + parts, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + + for (uint i = 0; i < poolTokenToDistribution.length; i++) { + poolTokenFromDistribution[i] |= poolTokenToDistribution[i] << 128; + } + + return (returnPoolTokenToAmount, poolTokenFromDistribution); + } + + if (isPoolTokenFrom) { + return _getExpectedReturnFromPoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _getExpectedReturnToPoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + } + + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + disableFlags + ); + } + + function _getExpectedReturnFromPoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + private + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + + distribution = new uint256[](DEXES_COUNT); + + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + + uint256 totalSupply = poolToken.totalSupply(); + + uint256 ethReserve = address(poolToken).balance; + uint256 ethAmount = amount.mul(ethReserve).div(totalSupply); + + if (!toToken.isETH()) { + (uint256 ret, uint256[] memory dist) = getExpectedReturn( + ETH_ADDRESS, + toToken, + ethAmount, + parts, + disableFlags + ); + + returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j]; + } + } else { + returnAmount = returnAmount.add(ethAmount); + } + + uint256 tokenReserve = uniswapToken.balanceOf(address(poolToken)); + uint256 exchangeTokenAmount = amount.mul(tokenReserve).div(totalSupply); + + if (toToken != uniswapToken) { + (uint256 ret, uint256[] memory dist) = getExpectedReturn( + uniswapToken, + toToken, + exchangeTokenAmount, + parts, + disableFlags + ); + + returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << 8; + } + } else { + returnAmount = returnAmount.add(exchangeTokenAmount); + } + + return (returnAmount, distribution); + } + + function _getExpectedReturnToPoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + private + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + + distribution = new uint256[](DEXES_COUNT); + + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + + uint256 totalSupply = poolToken.totalSupply(); + + uint256 ethReserve = address(poolToken).balance; + uint256 partAmountForEth = amount.div(2); + + if (!fromToken.isETH()) { + (uint256 ret, uint256[] memory dist) = getExpectedReturn( + fromToken, + ETH_ADDRESS, + partAmountForEth, + parts, + disableFlags + ); + + returnAmount = returnAmount.add( + ret.mul(totalSupply).div(ethReserve) + ); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j]; + } + } else { + returnAmount = returnAmount.add( + partAmountForEth.mul(totalSupply).div(ethReserve) + ); + } + + uint256 tokenReserve = uniswapToken.balanceOf(address(poolToken)); + uint256 partAmountForToken = amount.sub(partAmountForEth); + + if (fromToken != uniswapToken) { + (uint256 ret, uint256[] memory dist) = getExpectedReturn( + fromToken, + uniswapToken, + partAmountForToken, + parts, + disableFlags + ); + + returnAmount = returnAmount.add( + ret.mul(totalSupply).div(tokenReserve) + ); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << 8; + } + } else { + returnAmount = returnAmount.add( + partAmountForToken.mul(totalSupply).div(tokenReserve) + ); + } + + return (returnAmount, distribution); + } + +} + + +contract OneSplitUniswapPoolToken is OneSplitBase, OneSplitUniswapPoolTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 disableFlags + ) internal { + if (fromToken == toToken) { + return; + } + + if (!disableFlags.check(FLAG_DISABLE_UNISWAP_POOL_TOKEN)) { + bool isPoolTokenFrom = isLiquidityPool(fromToken); + bool isPoolTokenTo = isLiquidityPool(toToken); + + if (isPoolTokenFrom && isPoolTokenTo) { + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] & ((1 << 128) - 1); + } + + uint256 ethBalanceBefore = address(this).balance; + + _swapFromPoolToken( + fromToken, + ETH_ADDRESS, + amount, + dist, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] >> 128; + } + + uint256 ethBalanceAfter = address(this).balance; + + return _swapToPoolToken( + ETH_ADDRESS, + toToken, + ethBalanceAfter.sub(ethBalanceBefore), + dist, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + + if (isPoolTokenFrom) { + return _swapFromPoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _swapToPoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + } + + return super._swap( + fromToken, + toToken, + amount, + distribution, + disableFlags + ); + } + + function _swapFromPoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 disableFlags + ) private { + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + + uint256[] memory dist = new uint256[](distribution.length); + + ( + uint256 ethAmount, + uint256 exchangeTokenAmount + ) = IUniswapExchange(address(poolToken)).removeLiquidity( + amount, + 1, + 1, + now.add(1800) + ); + + if (!toToken.isETH()) { + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j]) & 0xFF; + } + + this.swap( + ETH_ADDRESS, + toToken, + ethAmount, + 0, + dist, + disableFlags + ); + } + + if (toToken != uniswapToken) { + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> 8) & 0xFF; + } + + this.swap( + uniswapToken, + toToken, + exchangeTokenAmount, + 0, + dist, + disableFlags + ); + } + } + + function _swapToPoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256[] memory distribution, + uint256 disableFlags + ) private { + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + + uint256 partAmountForEth = amount.div(2); + + uint256[] memory dist = new uint256[](distribution.length); + + if (!fromToken.isETH()) { + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j]) & 0xFF; + } + + this.swap( + fromToken, + ETH_ADDRESS, + partAmountForEth, + 0, + dist, + disableFlags + ); + } + + uint256 partAmountForToken = amount.sub(partAmountForEth); + + if (fromToken != uniswapToken) { + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> 8) & 0xFF; + } + + this.swap( + fromToken, + uniswapToken, + partAmountForToken, + 0, + dist, + disableFlags + ); + + _infiniteApproveIfNeeded(uniswapToken, address(poolToken)); + } + + uint256 maxTokens = uniswapToken.balanceOf(address(this)) + 1; + + IUniswapExchange(address(poolToken)).addLiquidity.value(address(this).balance)( + 1, // todo: think about another value + maxTokens, + now.add(1800) + ); + } +} diff --git a/contracts/interface/IUniswapExchange.sol b/contracts/interface/IUniswapExchange.sol index f1200c3..0196bd6 100644 --- a/contracts/interface/IUniswapExchange.sol +++ b/contracts/interface/IUniswapExchange.sol @@ -24,4 +24,8 @@ interface IUniswapExchange { uint256 deadline, address tokenAddr ) external returns (uint256 tokensBought); + + function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline) external payable returns (uint256); + + function removeLiquidity(uint256 amount, uint256 min_eth, uint256 min_tokens, uint256 deadline) external returns (uint256, uint256); } diff --git a/contracts/interface/IUniswapFactory.sol b/contracts/interface/IUniswapFactory.sol index bc352fc..14f9069 100644 --- a/contracts/interface/IUniswapFactory.sol +++ b/contracts/interface/IUniswapFactory.sol @@ -5,4 +5,6 @@ import "./IUniswapExchange.sol"; interface IUniswapFactory { function getExchange(IERC20 token) external view returns (IUniswapExchange exchange); + + function getToken(address exchange) external view returns (IERC20 token); } From e4849c9261bd609e00b0acd96913c18e1ac2ef78 Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 13 Apr 2020 03:43:44 +0300 Subject: [PATCH 34/60] modified formula and optimized --- contracts/OneSplitUniswapPoolToken.sol | 119 ++++++++++++++++--------- 1 file changed, 79 insertions(+), 40 deletions(-) diff --git a/contracts/OneSplitUniswapPoolToken.sol b/contracts/OneSplitUniswapPoolToken.sol index 8a5bcf1..c72a55d 100644 --- a/contracts/OneSplitUniswapPoolToken.sol +++ b/contracts/OneSplitUniswapPoolToken.sol @@ -14,6 +14,40 @@ contract OneSplitUniswapPoolTokenBase { return address(uniswapFactory.getToken(address(token))) != address(0); } + function getMaxPossibleFund( + IERC20 poolToken, + IERC20 uniswapToken, + uint256 tokenAmount, + uint256 existEthAmount + ) + internal + view + returns ( + uint256, + uint256 + ) + { + uint256 ethReserve = address(poolToken).balance; + uint256 totalLiquidity = poolToken.totalSupply(); + uint256 tokenReserve = uniswapToken.balanceOf(address(poolToken)); + + uint256 possibleEthAmount = ethReserve.mul( + tokenAmount.sub(1) + ).div(tokenReserve); + + if (existEthAmount > possibleEthAmount) { + return ( + possibleEthAmount, + possibleEthAmount.mul(totalLiquidity).div(ethReserve) + ); + } + + return ( + existEthAmount, + existEthAmount.mul(totalLiquidity).div(ethReserve) + ); + } + } contract OneSplitUniswapPoolTokenView is OneSplitBaseView, OneSplitUniswapPoolTokenBase { @@ -179,15 +213,12 @@ contract OneSplitUniswapPoolTokenView is OneSplitBaseView, OneSplitUniswapPoolTo distribution = new uint256[](DEXES_COUNT); - IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); - - uint256 totalSupply = poolToken.totalSupply(); + uint256[] memory dist = new uint256[](DEXES_COUNT); - uint256 ethReserve = address(poolToken).balance; + uint256 ethAmount; uint256 partAmountForEth = amount.div(2); - if (!fromToken.isETH()) { - (uint256 ret, uint256[] memory dist) = getExpectedReturn( + (ethAmount, dist) = super.getExpectedReturn( fromToken, ETH_ADDRESS, partAmountForEth, @@ -195,23 +226,19 @@ contract OneSplitUniswapPoolTokenView is OneSplitBaseView, OneSplitUniswapPoolTo disableFlags ); - returnAmount = returnAmount.add( - ret.mul(totalSupply).div(ethReserve) - ); for (uint j = 0; j < distribution.length; j++) { distribution[j] |= dist[j]; } } else { - returnAmount = returnAmount.add( - partAmountForEth.mul(totalSupply).div(ethReserve) - ); + ethAmount = partAmountForEth; } - uint256 tokenReserve = uniswapToken.balanceOf(address(poolToken)); - uint256 partAmountForToken = amount.sub(partAmountForEth); + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + uint256 tokenAmount; + uint256 partAmountForToken = amount.sub(partAmountForEth); if (fromToken != uniswapToken) { - (uint256 ret, uint256[] memory dist) = getExpectedReturn( + (tokenAmount, dist) = super.getExpectedReturn( fromToken, uniswapToken, partAmountForToken, @@ -219,19 +246,24 @@ contract OneSplitUniswapPoolTokenView is OneSplitBaseView, OneSplitUniswapPoolTo disableFlags ); - returnAmount = returnAmount.add( - ret.mul(totalSupply).div(tokenReserve) - ); for (uint j = 0; j < distribution.length; j++) { distribution[j] |= dist[j] << 8; } } else { - returnAmount = returnAmount.add( - partAmountForToken.mul(totalSupply).div(tokenReserve) - ); + tokenAmount = partAmountForToken; } - return (returnAmount, distribution); + (, returnAmount) = getMaxPossibleFund( + poolToken, + uniswapToken, + tokenAmount, + ethAmount + ); + + return ( + returnAmount, + distribution + ); } } @@ -321,7 +353,6 @@ contract OneSplitUniswapPoolToken is OneSplitBase, OneSplitUniswapPoolTokenBase uint256[] memory distribution, uint256 disableFlags ) private { - IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); uint256[] memory dist = new uint256[](distribution.length); @@ -340,26 +371,26 @@ contract OneSplitUniswapPoolToken is OneSplitBase, OneSplitUniswapPoolTokenBase dist[j] = (distribution[j]) & 0xFF; } - this.swap( + super._swap( ETH_ADDRESS, toToken, ethAmount, - 0, dist, disableFlags ); } + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + if (toToken != uniswapToken) { for (uint j = 0; j < distribution.length; j++) { dist[j] = (distribution[j] >> 8) & 0xFF; } - this.swap( + super._swap( uniswapToken, toToken, exchangeTokenAmount, - 0, dist, disableFlags ); @@ -373,39 +404,35 @@ contract OneSplitUniswapPoolToken is OneSplitBase, OneSplitUniswapPoolTokenBase uint256[] memory distribution, uint256 disableFlags ) private { - IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); - - uint256 partAmountForEth = amount.div(2); - uint256[] memory dist = new uint256[](distribution.length); + uint256 partAmountForEth = amount.div(2); if (!fromToken.isETH()) { for (uint j = 0; j < distribution.length; j++) { dist[j] = (distribution[j]) & 0xFF; } - this.swap( + super._swap( fromToken, ETH_ADDRESS, partAmountForEth, - 0, dist, disableFlags ); } - uint256 partAmountForToken = amount.sub(partAmountForEth); + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + uint256 partAmountForToken = amount.sub(partAmountForEth); if (fromToken != uniswapToken) { for (uint j = 0; j < distribution.length; j++) { dist[j] = (distribution[j] >> 8) & 0xFF; } - this.swap( + super._swap( fromToken, uniswapToken, partAmountForToken, - 0, dist, disableFlags ); @@ -413,12 +440,24 @@ contract OneSplitUniswapPoolToken is OneSplitBase, OneSplitUniswapPoolTokenBase _infiniteApproveIfNeeded(uniswapToken, address(poolToken)); } - uint256 maxTokens = uniswapToken.balanceOf(address(this)) + 1; + uint256 ethBalance = address(this).balance; + uint256 tokenBalance = uniswapToken.balanceOf(address(this)); - IUniswapExchange(address(poolToken)).addLiquidity.value(address(this).balance)( - 1, // todo: think about another value - maxTokens, + (uint256 ethAmount, uint256 returnAmount) = getMaxPossibleFund( + poolToken, + uniswapToken, + tokenBalance, + ethBalance + ); + + IUniswapExchange(address(poolToken)).addLiquidity.value(ethAmount)( + returnAmount.mul(995).div(1000), + uint256(-1), // todo: think about another value now.add(1800) ); + + // todo: do we need to check difference between balance before and balance after? + uniswapToken.universalTransfer(msg.sender, uniswapToken.balanceOf(address(this))); + ETH_ADDRESS.universalTransfer(msg.sender, address(this).balance); } } From 82e511be0d8f44dca465a46192d302c387acde39 Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 13 Apr 2020 03:44:22 +0300 Subject: [PATCH 35/60] add comment --- contracts/OneSplitUniswapPoolToken.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/OneSplitUniswapPoolToken.sol b/contracts/OneSplitUniswapPoolToken.sol index c72a55d..63e7632 100644 --- a/contracts/OneSplitUniswapPoolToken.sol +++ b/contracts/OneSplitUniswapPoolToken.sol @@ -451,8 +451,8 @@ contract OneSplitUniswapPoolToken is OneSplitBase, OneSplitUniswapPoolTokenBase ); IUniswapExchange(address(poolToken)).addLiquidity.value(ethAmount)( - returnAmount.mul(995).div(1000), - uint256(-1), // todo: think about another value + returnAmount.mul(995).div(1000), // 0.5% slippage + uint256(-1), // todo: think about another value now.add(1800) ); From c6705673e40c475b06f6a0c070361d91d964b0ed Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 21 Apr 2020 15:25:43 +0300 Subject: [PATCH 36/60] done with view contract --- contracts/IOneSplit.sol | 1 + contracts/OneSplit.sol | 4 +- contracts/OneSplitBalancerPoolToken.sol | 450 ++++++++++++++++++++++++ contracts/interface/IBFactory.sol | 5 + contracts/interface/IBPool.sol | 34 ++ 5 files changed, 493 insertions(+), 1 deletion(-) create mode 100644 contracts/OneSplitBalancerPoolToken.sol create mode 100644 contracts/interface/IBFactory.sol create mode 100644 contracts/interface/IBPool.sol diff --git a/contracts/IOneSplit.sol b/contracts/IOneSplit.sol index c2d3fa9..e570d54 100644 --- a/contracts/IOneSplit.sol +++ b/contracts/IOneSplit.sol @@ -33,6 +33,7 @@ contract IOneSplitConsts { uint256 public constant FLAG_ENABLE_UNISWAP_AAVE = 0x400000; // Works only when one of assets is ETH or FLAG_ENABLE_MULTI_PATH_ETH uint256 public constant FLAG_DISABLE_IDLE = 0x800000; uint256 public constant FLAG_DISABLE_UNISWAP_POOL_TOKEN = 0x1000000; + uint256 public constant FLAG_DISABLE_BALANCER_POOL_TOKEN = 0x2000000; } diff --git a/contracts/OneSplit.sol b/contracts/OneSplit.sol index 7633cac..65f2c65 100644 --- a/contracts/OneSplit.sol +++ b/contracts/OneSplit.sol @@ -12,6 +12,7 @@ import "./OneSplitIdle.sol"; import "./OneSplitAave.sol"; import "./OneSplitWeth.sol"; import "./OneSplitUniswapPoolToken.sol"; +import "./OneSplitBalancerPoolToken.sol"; //import "./OneSplitSmartToken.sol"; @@ -27,7 +28,8 @@ contract OneSplitView is OneSplitIearnView, OneSplitIdleView(0x23E4D1536c449e4D79E5903B4A9ddc3655be8609), OneSplitWethView, - OneSplitUniswapPoolTokenView + OneSplitUniswapPoolTokenView, + OneSplitBalancerPoolTokenView //OneSplitSmartTokenView { function() external { diff --git a/contracts/OneSplitBalancerPoolToken.sol b/contracts/OneSplitBalancerPoolToken.sol new file mode 100644 index 0000000..d25806e --- /dev/null +++ b/contracts/OneSplitBalancerPoolToken.sol @@ -0,0 +1,450 @@ +pragma solidity ^0.5.0; + +import "./OneSplitBase.sol"; +import "./interface/IBFactory.sol"; +import "./interface/IBPool.sol"; + + +contract OneSplitBalancerPoolTokenBase { + using SafeMath for uint256; + + // todo: factory for Bronze release + // may be changed in future + IBFactory bFactory = IBFactory(0x9424B1412450D0f8Fc2255FAf6046b98213B76Bd); + + struct TokenWithWeight { + IERC20 token; + uint256 reserveBalance; + uint256 denormalizedWeight; + } + + struct PoolTokenDetails { + TokenWithWeight[] tokens; + uint256 totalWeight; + uint256 totalSupply; + } + + function _getPoolDetails(IBPool poolToken) + internal + view + returns(PoolTokenDetails memory details) + { + address[] memory currentTokens = poolToken.getCurrentTokens(); + details.tokens = new TokenWithWeight[](currentTokens.length); + details.totalWeight = poolToken.getTotalDenormalizedWeight(); + details.totalSupply = poolToken.totalSupply(); + for (uint256 i = 0; i < details.tokens.length; i++) { + details.tokens[i].token = IERC20(currentTokens[i]); + details.tokens[i].denormalizedWeight = poolToken.getDenormalizedWeight(currentTokens[i]); + details.tokens[i].reserveBalance = poolToken.getBalance(currentTokens[i]); + } + } + +} + +contract OneSplitBalancerPoolTokenView is OneSplitBaseView, OneSplitBalancerPoolTokenBase { + + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + internal + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) + { + if (fromToken == toToken) { + return (amount, new uint256[](DEXES_COUNT)); + } + + + if (!disableFlags.check(FLAG_DISABLE_BALANCER_POOL_TOKEN)) { + bool isPoolTokenFrom = bFactory.isBPool(address(fromToken)); + bool isPoolTokenTo = bFactory.isBPool(address(toToken)); + + if (isPoolTokenFrom && isPoolTokenTo) { + ( + uint256 returnETHAmount, + uint256[] memory poolTokenFromDistribution + ) = _getExpectedReturnFromBalancerPoolToken( + fromToken, + ETH_ADDRESS, + amount, + parts, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + + ( + uint256 returnPoolTokenToAmount, + uint256[] memory poolTokenToDistribution + ) = _getExpectedReturnToBalancerPoolToken( + ETH_ADDRESS, + toToken, + returnETHAmount, + parts, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + + for (uint i = 0; i < poolTokenToDistribution.length; i++) { + poolTokenFromDistribution[i] |= poolTokenToDistribution[i] << 128; + } + + return (returnPoolTokenToAmount, poolTokenFromDistribution); + } + + if (isPoolTokenFrom) { + return _getExpectedReturnFromBalancerPoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _getExpectedReturnToBalancerPoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + } + } + + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + disableFlags + ); + } + + function _getExpectedReturnFromBalancerPoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + private + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + IBPool bToken = IBPool(address(poolToken)); + address[] memory currentTokens = bToken.getCurrentTokens(); + + uint256 pAiAfterExitFee = amount.sub( + amount.mul(bToken.EXIT_FEE()) + ); + uint256 ratio = pAiAfterExitFee.mul(1e18).div(poolToken.totalSupply()); + for (uint i = 0; i < currentTokens.length; i++) { + uint256 tokenAmountOut = bToken.getBalance(currentTokens[i]).mul(ratio).div(1e18); + + if (currentTokens[i] == address(toToken)) { + returnAmount = returnAmount.add(tokenAmountOut); + continue; + } + + (uint256 ret, uint256[] memory dist) = getExpectedReturn( + IERC20(currentTokens[i]), + toToken, + tokenAmountOut, + parts, + disableFlags + ); + + returnAmount = returnAmount.add(ret); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << 8; + } + } + + return (returnAmount, distribution); + } + + function _getExpectedReturnToBalancerPoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + private + returns ( + uint256 minFundAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + minFundAmount = uint256(-1); + + PoolTokenDetails memory details = _getPoolDetails(IBPool(address(poolToken))); + + uint256[] memory tokenAmounts = new uint256[](details.tokens.length); + uint256[] memory dist; + uint256[] memory fundAmounts = new uint256[](details.tokens.length); + + for (uint i = 0; i < details.tokens.length; i++) { + uint256 exchangeAmount = amount.mul( + details.tokens[i].denormalizedWeight + ).div(details.totalWeight); + + if (details.tokens[i].token != fromToken) { + (tokenAmounts[i], dist) = getExpectedReturn( + fromToken, + details.tokens[i].token, + exchangeAmount, + parts, + disableFlags + ); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << 8; + } + } else { + tokenAmounts[i] = exchangeAmount; + } + + fundAmounts[i] = tokenAmounts[i] + .mul(details.totalSupply) + .div(details.tokens[i].reserveBalance); + + if (fundAmounts[i] < minFundAmount) { + minFundAmount = fundAmounts[i]; + } + } + + uint256 _minFundAmount = minFundAmount; + uint256 swapFee = IBPool(address(poolToken)).getSwapFee(); + // Swap leftovers for PoolToken + for (uint i = 0; i < details.tokens.length; i++) { + if (_minFundAmount == fundAmounts[i]) { + continue; + } + + uint256 leftover = tokenAmounts[i].sub( + fundAmounts[i].mul(details.tokens[i].reserveBalance).div(details.totalSupply) + ); + + uint256 tokenRet = IBPool(address(poolToken)).calcPoolOutGivenSingleIn( + details.tokens[i].reserveBalance, + details.tokens[i].denormalizedWeight, + details.totalSupply, + details.totalWeight, + leftover, + swapFee + ); + + minFundAmount = minFundAmount.add(tokenRet); + } + + return (minFundAmount, distribution); + } + +} + + +//contract OneSplitBalancerPoolToken is OneSplitBase, OneSplitBalancerPoolTokenBase { +// function _swap( +// IERC20 fromToken, +// IERC20 toToken, +// uint256 amount, +// uint256[] memory distribution, +// uint256 disableFlags +// ) internal { +// if (fromToken == toToken) { +// return; +// } +// +// if (!disableFlags.check(FLAG_DISABLE_BALANCER_POOL_TOKEN)) { +// bool isPoolTokenFrom = isLiquidityPool(fromToken); +// bool isPoolTokenTo = isLiquidityPool(toToken); +// +// if (isPoolTokenFrom && isPoolTokenTo) { +// uint256[] memory dist = new uint256[](distribution.length); +// for (uint i = 0; i < distribution.length; i++) { +// dist[i] = distribution[i] & ((1 << 128) - 1); +// } +// +// uint256 ethBalanceBefore = address(this).balance; +// +// _swapFromPoolToken( +// fromToken, +// ETH_ADDRESS, +// amount, +// dist, +// FLAG_DISABLE_UNISWAP_POOL_TOKEN +// ); +// +// for (uint i = 0; i < distribution.length; i++) { +// dist[i] = distribution[i] >> 128; +// } +// +// uint256 ethBalanceAfter = address(this).balance; +// +// return _swapToPoolToken( +// ETH_ADDRESS, +// toToken, +// ethBalanceAfter.sub(ethBalanceBefore), +// dist, +// FLAG_DISABLE_UNISWAP_POOL_TOKEN +// ); +// } +// +// if (isPoolTokenFrom) { +// return _swapFromPoolToken( +// fromToken, +// toToken, +// amount, +// distribution, +// FLAG_DISABLE_UNISWAP_POOL_TOKEN +// ); +// } +// +// if (isPoolTokenTo) { +// return _swapToPoolToken( +// fromToken, +// toToken, +// amount, +// distribution, +// FLAG_DISABLE_UNISWAP_POOL_TOKEN +// ); +// } +// } +// +// return super._swap( +// fromToken, +// toToken, +// amount, +// distribution, +// disableFlags +// ); +// } +// +// function _swapFromPoolToken( +// IERC20 poolToken, +// IERC20 toToken, +// uint256 amount, +// uint256[] memory distribution, +// uint256 disableFlags +// ) private { +// +// uint256[] memory dist = new uint256[](distribution.length); +// +// ( +// uint256 ethAmount, +// uint256 exchangeTokenAmount +// ) = IUniswapExchange(address(poolToken)).removeLiquidity( +// amount, +// 1, +// 1, +// now.add(1800) +// ); +// +// if (!toToken.isETH()) { +// for (uint j = 0; j < distribution.length; j++) { +// dist[j] = (distribution[j]) & 0xFF; +// } +// +// super._swap( +// ETH_ADDRESS, +// toToken, +// ethAmount, +// dist, +// disableFlags +// ); +// } +// +// IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); +// +// if (toToken != uniswapToken) { +// for (uint j = 0; j < distribution.length; j++) { +// dist[j] = (distribution[j] >> 8) & 0xFF; +// } +// +// super._swap( +// uniswapToken, +// toToken, +// exchangeTokenAmount, +// dist, +// disableFlags +// ); +// } +// } +// +// function _swapToPoolToken( +// IERC20 fromToken, +// IERC20 poolToken, +// uint256 amount, +// uint256[] memory distribution, +// uint256 disableFlags +// ) private { +// uint256[] memory dist = new uint256[](distribution.length); +// +// uint256 partAmountForEth = amount.div(2); +// if (!fromToken.isETH()) { +// for (uint j = 0; j < distribution.length; j++) { +// dist[j] = (distribution[j]) & 0xFF; +// } +// +// super._swap( +// fromToken, +// ETH_ADDRESS, +// partAmountForEth, +// dist, +// disableFlags +// ); +// } +// +// IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); +// +// uint256 partAmountForToken = amount.sub(partAmountForEth); +// if (fromToken != uniswapToken) { +// for (uint j = 0; j < distribution.length; j++) { +// dist[j] = (distribution[j] >> 8) & 0xFF; +// } +// +// super._swap( +// fromToken, +// uniswapToken, +// partAmountForToken, +// dist, +// disableFlags +// ); +// +// _infiniteApproveIfNeeded(uniswapToken, address(poolToken)); +// } +// +// uint256 ethBalance = address(this).balance; +// uint256 tokenBalance = uniswapToken.balanceOf(address(this)); +// +// (uint256 ethAmount, uint256 returnAmount) = getMaxPossibleFund( +// poolToken, +// uniswapToken, +// tokenBalance, +// ethBalance +// ); +// +// IUniswapExchange(address(poolToken)).addLiquidity.value(ethAmount)( +// returnAmount.mul(995).div(1000), // 0.5% slippage +// uint256(- 1), // todo: think about another value +// now.add(1800) +// ); +// +// // todo: do we need to check difference between balance before and balance after? +// uniswapToken.universalTransfer(msg.sender, uniswapToken.balanceOf(address(this))); +// ETH_ADDRESS.universalTransfer(msg.sender, address(this).balance); +// } +//} diff --git a/contracts/interface/IBFactory.sol b/contracts/interface/IBFactory.sol new file mode 100644 index 0000000..f89d171 --- /dev/null +++ b/contracts/interface/IBFactory.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.5.0; + +interface IBFactory { + function isBPool(address b) external view returns (bool); +} diff --git a/contracts/interface/IBPool.sol b/contracts/interface/IBPool.sol new file mode 100644 index 0000000..20bcd08 --- /dev/null +++ b/contracts/interface/IBPool.sol @@ -0,0 +1,34 @@ +pragma solidity ^0.5.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract BConst { + uint public constant EXIT_FEE = 0; +} + +contract IBMath is BConst { + function calcPoolOutGivenSingleIn( + uint tokenBalanceIn, + uint tokenWeightIn, + uint poolSupply, + uint totalWeight, + uint tokenAmountIn, + uint swapFee + ) + public + pure returns (uint poolAmountOut); +} + +contract IBPool is IERC20, IBMath { + function getCurrentTokens() external view returns (address[] memory tokens); + + function getBalance(address token) external view returns (uint); + + function getNormalizedWeight(address token) external view returns (uint); + + function getDenormalizedWeight(address token) external view returns (uint); + + function getTotalDenormalizedWeight() external view returns (uint); + + function getSwapFee() external view returns (uint); +} From ae2651a4886d331c369bc34ccbc72a6f9458b498 Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 21 Apr 2020 23:22:32 +0300 Subject: [PATCH 37/60] almost done with swap --- contracts/OneSplit.sol | 3 +- contracts/OneSplitBalancerPoolToken.sol | 440 ++++++++++++------------ contracts/interface/IBPool.sol | 6 + 3 files changed, 226 insertions(+), 223 deletions(-) diff --git a/contracts/OneSplit.sol b/contracts/OneSplit.sol index 65f2c65..8995a4c 100644 --- a/contracts/OneSplit.sol +++ b/contracts/OneSplit.sol @@ -103,7 +103,8 @@ contract OneSplit is OneSplitIearn, OneSplitIdle(0x23E4D1536c449e4D79E5903B4A9ddc3655be8609), OneSplitWeth, - OneSplitUniswapPoolToken + OneSplitUniswapPoolToken, + OneSplitBalancerPoolToken //OneSplitSmartToken { IOneSplitView public oneSplitView; diff --git a/contracts/OneSplitBalancerPoolToken.sol b/contracts/OneSplitBalancerPoolToken.sol index d25806e..e04b33c 100644 --- a/contracts/OneSplitBalancerPoolToken.sol +++ b/contracts/OneSplitBalancerPoolToken.sol @@ -51,11 +51,11 @@ contract OneSplitBalancerPoolTokenView is OneSplitBaseView, OneSplitBalancerPool uint256 parts, uint256 disableFlags ) - internal - returns ( - uint256 returnAmount, - uint256[] memory distribution - ) + internal + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) { if (fromToken == toToken) { return (amount, new uint256[](DEXES_COUNT)); @@ -133,11 +133,11 @@ contract OneSplitBalancerPoolTokenView is OneSplitBaseView, OneSplitBalancerPool uint256 parts, uint256 disableFlags ) - private - returns ( - uint256 returnAmount, - uint256[] memory distribution - ) + private + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) { distribution = new uint256[](DEXES_COUNT); @@ -167,7 +167,7 @@ contract OneSplitBalancerPoolTokenView is OneSplitBaseView, OneSplitBalancerPool returnAmount = returnAmount.add(ret); for (uint j = 0; j < distribution.length; j++) { - distribution[j] |= dist[j] << 8; + distribution[j] |= dist[j] << (i * 8); } } @@ -211,7 +211,7 @@ contract OneSplitBalancerPoolTokenView is OneSplitBaseView, OneSplitBalancerPool ); for (uint j = 0; j < distribution.length; j++) { - distribution[j] |= dist[j] << 8; + distribution[j] |= dist[j] << (i * 8); } } else { tokenAmounts[i] = exchangeAmount; @@ -226,225 +226,221 @@ contract OneSplitBalancerPoolTokenView is OneSplitBaseView, OneSplitBalancerPool } } - uint256 _minFundAmount = minFundAmount; - uint256 swapFee = IBPool(address(poolToken)).getSwapFee(); +// uint256 _minFundAmount = minFundAmount; +// uint256 swapFee = IBPool(address(poolToken)).getSwapFee(); // Swap leftovers for PoolToken - for (uint i = 0; i < details.tokens.length; i++) { - if (_minFundAmount == fundAmounts[i]) { +// for (uint i = 0; i < details.tokens.length; i++) { +// if (_minFundAmount == fundAmounts[i]) { +// continue; +// } +// +// uint256 leftover = tokenAmounts[i].sub( +// fundAmounts[i].mul(details.tokens[i].reserveBalance).div(details.totalSupply) +// ); +// +// uint256 tokenRet = IBPool(address(poolToken)).calcPoolOutGivenSingleIn( +// details.tokens[i].reserveBalance, +// details.tokens[i].denormalizedWeight, +// details.totalSupply, +// details.totalWeight, +// leftover, +// swapFee +// ); +// +// minFundAmount = minFundAmount.add(tokenRet); +// } + + return (minFundAmount, distribution); + } + +} + + +contract OneSplitBalancerPoolToken is OneSplitBase, OneSplitBalancerPoolTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 disableFlags + ) internal { + if (fromToken == toToken) { + return; + } + + if (!disableFlags.check(FLAG_DISABLE_BALANCER_POOL_TOKEN)) { + bool isPoolTokenFrom = bFactory.isBPool(address(fromToken)); + bool isPoolTokenTo = bFactory.isBPool(address(toToken)); + + if (isPoolTokenFrom && isPoolTokenTo) { + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] & ((1 << 128) - 1); + } + + uint256 ethBalanceBefore = address(this).balance; + + _swapFromBalancerPoolToken( + fromToken, + ETH_ADDRESS, + amount, + dist, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] >> 128; + } + + uint256 ethBalanceAfter = address(this).balance; + + return _swapToBalancerPoolToken( + ETH_ADDRESS, + toToken, + ethBalanceAfter.sub(ethBalanceBefore), + dist, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + } + + if (isPoolTokenFrom) { + return _swapFromBalancerPoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _swapToBalancerPoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + } + } + + return super._swap( + fromToken, + toToken, + amount, + distribution, + disableFlags + ); + } + + function _swapFromBalancerPoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 disableFlags + ) private { + + IBPool bToken = IBPool(address(poolToken)); + + address[] memory currentTokens = bToken.getCurrentTokens(); + + uint256 ratio = amount.sub( + amount.mul(bToken.EXIT_FEE()) + ).mul(1e18).div(poolToken.totalSupply()); + + uint256[] memory minAmountsOut = new uint256[](currentTokens.length); + for (uint i = 0; i < currentTokens.length; i++) { + minAmountsOut[i] = bToken.getBalance(currentTokens[i]).mul(ratio).div(1e18).mul(995).div(1000); // 0.5% slippage; + } + + bToken.exitPool(amount, minAmountsOut); + + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < currentTokens.length; i++) { + + if (currentTokens[i] == address(toToken)) { continue; } - uint256 leftover = tokenAmounts[i].sub( - fundAmounts[i].mul(details.tokens[i].reserveBalance).div(details.totalSupply) - ); + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } - uint256 tokenRet = IBPool(address(poolToken)).calcPoolOutGivenSingleIn( - details.tokens[i].reserveBalance, - details.tokens[i].denormalizedWeight, - details.totalSupply, - details.totalWeight, - leftover, - swapFee - ); + uint256 exchangeTokenAmount = IERC20(currentTokens[i]).balanceOf(address(this)); - minFundAmount = minFundAmount.add(tokenRet); + this.swap( + IERC20(currentTokens[i]), + toToken, + exchangeTokenAmount, + 0, + dist, + disableFlags + ); } - return (minFundAmount, distribution); } -} + function _swapToBalancerPoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256[] memory distribution, + uint256 disableFlags + ) private { + uint256[] memory dist = new uint256[](distribution.length); + uint256 minFundAmount = uint256(-1); + PoolTokenDetails memory details = _getPoolDetails(IBPool(address(poolToken))); -//contract OneSplitBalancerPoolToken is OneSplitBase, OneSplitBalancerPoolTokenBase { -// function _swap( -// IERC20 fromToken, -// IERC20 toToken, -// uint256 amount, -// uint256[] memory distribution, -// uint256 disableFlags -// ) internal { -// if (fromToken == toToken) { -// return; -// } -// -// if (!disableFlags.check(FLAG_DISABLE_BALANCER_POOL_TOKEN)) { -// bool isPoolTokenFrom = isLiquidityPool(fromToken); -// bool isPoolTokenTo = isLiquidityPool(toToken); -// -// if (isPoolTokenFrom && isPoolTokenTo) { -// uint256[] memory dist = new uint256[](distribution.length); -// for (uint i = 0; i < distribution.length; i++) { -// dist[i] = distribution[i] & ((1 << 128) - 1); -// } -// -// uint256 ethBalanceBefore = address(this).balance; -// -// _swapFromPoolToken( -// fromToken, -// ETH_ADDRESS, -// amount, -// dist, -// FLAG_DISABLE_UNISWAP_POOL_TOKEN -// ); -// -// for (uint i = 0; i < distribution.length; i++) { -// dist[i] = distribution[i] >> 128; -// } -// -// uint256 ethBalanceAfter = address(this).balance; -// -// return _swapToPoolToken( -// ETH_ADDRESS, -// toToken, -// ethBalanceAfter.sub(ethBalanceBefore), -// dist, -// FLAG_DISABLE_UNISWAP_POOL_TOKEN -// ); -// } -// -// if (isPoolTokenFrom) { -// return _swapFromPoolToken( -// fromToken, -// toToken, -// amount, -// distribution, -// FLAG_DISABLE_UNISWAP_POOL_TOKEN -// ); -// } -// -// if (isPoolTokenTo) { -// return _swapToPoolToken( -// fromToken, -// toToken, -// amount, -// distribution, -// FLAG_DISABLE_UNISWAP_POOL_TOKEN -// ); -// } -// } -// -// return super._swap( -// fromToken, -// toToken, -// amount, -// distribution, -// disableFlags -// ); -// } -// -// function _swapFromPoolToken( -// IERC20 poolToken, -// IERC20 toToken, -// uint256 amount, -// uint256[] memory distribution, -// uint256 disableFlags -// ) private { -// -// uint256[] memory dist = new uint256[](distribution.length); -// -// ( -// uint256 ethAmount, -// uint256 exchangeTokenAmount -// ) = IUniswapExchange(address(poolToken)).removeLiquidity( -// amount, -// 1, -// 1, -// now.add(1800) -// ); -// -// if (!toToken.isETH()) { -// for (uint j = 0; j < distribution.length; j++) { -// dist[j] = (distribution[j]) & 0xFF; -// } -// -// super._swap( -// ETH_ADDRESS, -// toToken, -// ethAmount, -// dist, -// disableFlags -// ); -// } -// -// IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); -// -// if (toToken != uniswapToken) { -// for (uint j = 0; j < distribution.length; j++) { -// dist[j] = (distribution[j] >> 8) & 0xFF; -// } -// -// super._swap( -// uniswapToken, -// toToken, -// exchangeTokenAmount, -// dist, -// disableFlags -// ); -// } -// } -// -// function _swapToPoolToken( -// IERC20 fromToken, -// IERC20 poolToken, -// uint256 amount, -// uint256[] memory distribution, -// uint256 disableFlags -// ) private { -// uint256[] memory dist = new uint256[](distribution.length); -// -// uint256 partAmountForEth = amount.div(2); -// if (!fromToken.isETH()) { -// for (uint j = 0; j < distribution.length; j++) { -// dist[j] = (distribution[j]) & 0xFF; -// } -// -// super._swap( -// fromToken, -// ETH_ADDRESS, -// partAmountForEth, -// dist, -// disableFlags -// ); -// } -// -// IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); -// -// uint256 partAmountForToken = amount.sub(partAmountForEth); -// if (fromToken != uniswapToken) { -// for (uint j = 0; j < distribution.length; j++) { -// dist[j] = (distribution[j] >> 8) & 0xFF; -// } -// -// super._swap( -// fromToken, -// uniswapToken, -// partAmountForToken, -// dist, -// disableFlags -// ); -// -// _infiniteApproveIfNeeded(uniswapToken, address(poolToken)); -// } -// -// uint256 ethBalance = address(this).balance; -// uint256 tokenBalance = uniswapToken.balanceOf(address(this)); -// -// (uint256 ethAmount, uint256 returnAmount) = getMaxPossibleFund( -// poolToken, -// uniswapToken, -// tokenBalance, -// ethBalance -// ); -// -// IUniswapExchange(address(poolToken)).addLiquidity.value(ethAmount)( -// returnAmount.mul(995).div(1000), // 0.5% slippage -// uint256(- 1), // todo: think about another value -// now.add(1800) -// ); -// -// // todo: do we need to check difference between balance before and balance after? -// uniswapToken.universalTransfer(msg.sender, uniswapToken.balanceOf(address(this))); -// ETH_ADDRESS.universalTransfer(msg.sender, address(this).balance); -// } -//} + uint256[] memory maxAmountsIn = new uint256[](details.tokens.length); + uint256 curFundAmount; + for (uint i = 0; i < details.tokens.length; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].denormalizedWeight) + .div(details.totalWeight); + + if (details.tokens[i].token != fromToken) { + uint256 tokenBalanceBefore = details.tokens[i].token.balanceOf(address(this)); + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + this.swap( + fromToken, + details.tokens[i].token, + exchangeAmount, + 0, + dist, + disableFlags + ); + + uint256 tokenBalanceAfter = details.tokens[i].token.balanceOf(address(this)); + + curFundAmount = ( + tokenBalanceAfter.sub(tokenBalanceBefore) + ).mul(details.totalSupply).div(details.tokens[i].reserveBalance); + } else { + curFundAmount = ( + exchangeAmount + ).mul(details.totalSupply).div(details.tokens[i].reserveBalance); + } + + if (curFundAmount < minFundAmount) { + minFundAmount = curFundAmount; + } + + maxAmountsIn[i] = uint256(-1); + _infiniteApproveIfNeeded(details.tokens[i].token, address(poolToken)); + } + + // todo: check for vulnerability + IBPool(address(poolToken)).joinPool(minFundAmount, maxAmountsIn); + + // Return leftovers + for (uint i = 0; i < details.tokens.length; i++) { + details.tokens[i].token.universalTransfer(msg.sender, details.tokens[i].token.balanceOf(address(this))); + } + } +} diff --git a/contracts/interface/IBPool.sol b/contracts/interface/IBPool.sol index 20bcd08..42d4dd5 100644 --- a/contracts/interface/IBPool.sol +++ b/contracts/interface/IBPool.sol @@ -20,6 +20,12 @@ contract IBMath is BConst { } contract IBPool is IERC20, IBMath { + function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn) external; + + function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut) external; + + function joinswapExternAmountIn(address tokenIn, uint tokenAmountIn, uint minPoolAmountOut) external returns (uint poolAmountOut); + function getCurrentTokens() external view returns (address[] memory tokens); function getBalance(address token) external view returns (uint); From b50e5a1df3d1e436b97d2b51a8d3afcdb84ee95e Mon Sep 17 00:00:00 2001 From: Kirill Date: Sun, 26 Apr 2020 22:58:19 +0300 Subject: [PATCH 38/60] almost done with curve susd pool token --- contracts/IOneSplit.sol | 1 + contracts/OneSplitCurveSusdPoolToken.sol | 314 +++++++++++++++++++++++ contracts/interface/ICurve.sol | 13 + 3 files changed, 328 insertions(+) create mode 100644 contracts/OneSplitCurveSusdPoolToken.sol diff --git a/contracts/IOneSplit.sol b/contracts/IOneSplit.sol index e570d54..8ad5301 100644 --- a/contracts/IOneSplit.sol +++ b/contracts/IOneSplit.sol @@ -34,6 +34,7 @@ contract IOneSplitConsts { uint256 public constant FLAG_DISABLE_IDLE = 0x800000; uint256 public constant FLAG_DISABLE_UNISWAP_POOL_TOKEN = 0x1000000; uint256 public constant FLAG_DISABLE_BALANCER_POOL_TOKEN = 0x2000000; + uint256 public constant FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN = 0x4000000; } diff --git a/contracts/OneSplitCurveSusdPoolToken.sol b/contracts/OneSplitCurveSusdPoolToken.sol new file mode 100644 index 0000000..f9bbb01 --- /dev/null +++ b/contracts/OneSplitCurveSusdPoolToken.sol @@ -0,0 +1,314 @@ +pragma solidity ^0.5.0; + +import "./OneSplitBase.sol"; +import "./interface/ICurve.sol"; + + +contract OneSplitCurveSusdPoolTokenBase { + using SafeMath for uint256; + using UniversalERC20 for IERC20; + + ISusdCurve curve = ISusdCurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); + IERC20 curveSusdToken = IERC20(0xC25a3A3b969415c80451098fa907EC722572917F); + + struct TokenInfo { + IERC20 token; + uint256 reserveBalance; + } + + struct PoolTokenDetails { + TokenInfo[] tokens; + uint256 totalSupply; + } + + function _getPoolDetails() + internal + view + returns(PoolTokenDetails memory details) + { + details.tokens = new TokenInfo[](4); + details.totalSupply = curveSusdToken.totalSupply(); + for (uint256 i = 0; i < 4; i++) { + details.tokens[i].token = IERC20(curve.coins(int128(i))); + details.tokens[i].reserveBalance = curve.balances(int128(i)); + } + } + +} + +contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPoolTokenBase { + + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + internal + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) + { + if (fromToken == toToken) { + return (amount, new uint256[](DEXES_COUNT)); + } + + + if (!disableFlags.check(FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN)) { + if (fromToken == curveSusdToken) { + return _getExpectedReturnFromCurveSusdPoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN + ); + } + + if (toToken == curveSusdToken) { + return _getExpectedReturnToCurveSusdPoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN + ); + } + } + + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + disableFlags + ); + } + + function _getExpectedReturnFromCurveSusdPoolToken( + IERC20, // poolToken + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + private + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + PoolTokenDetails memory details = _getPoolDetails(); + + uint256 ratio = amount.mul(1e18).div(details.totalSupply); + for (uint i = 0; i < 4; i++) { + uint256 tokenAmountOut = details.tokens[i].reserveBalance.mul(ratio).div(1e18); + + if (details.tokens[i].token == toToken) { + returnAmount = returnAmount.add(tokenAmountOut); + continue; + } + + (uint256 ret, uint256[] memory dist) = getExpectedReturn( + details.tokens[i].token, + toToken, + tokenAmountOut, + parts, + disableFlags + ); + + returnAmount = returnAmount.add(ret); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + return (returnAmount, distribution); + } + + function _getExpectedReturnToCurveSusdPoolToken( + IERC20 fromToken, + IERC20, // poolToken + uint256 amount, + uint256 parts, + uint256 disableFlags + ) + private + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + PoolTokenDetails memory details = _getPoolDetails(); + + uint256[4] memory tokenAmounts; + uint256[] memory dist; + uint256 exchangeAmountPart = amount.div(4); + for (uint i = 0; i < 4; i++) { + + if (details.tokens[i].token != fromToken) { + (tokenAmounts[i], dist) = getExpectedReturn( + fromToken, + details.tokens[i].token, + i != 3 ? exchangeAmountPart : exchangeAmountPart.add(exchangeAmountPart % 4), + parts, + disableFlags + ); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } else { + tokenAmounts[i] = i != 3 ? exchangeAmountPart : exchangeAmountPart.add(exchangeAmountPart % 4); + } + } + + returnAmount = curve.calc_token_amount(tokenAmounts, true); + + return (returnAmount, distribution); + } + +} + + +contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 disableFlags + ) internal { + if (fromToken == toToken) { + return; + } + + if (!disableFlags.check(FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN)) { + if (fromToken == curveSusdToken) { + return _swapFromCurveSusdPoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN + ); + } + + if (toToken == curveSusdToken) { + return _swapToCurveSusdPoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN + ); + } + } + + return super._swap( + fromToken, + toToken, + amount, + distribution, + disableFlags + ); + } + + function _swapFromCurveSusdPoolToken( + IERC20, // poolToken + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 disableFlags + ) private { + + PoolTokenDetails memory details = _getPoolDetails(); + + uint256 ratio = amount.mul(1e18).div(details.totalSupply); + + uint256[4] memory minAmountsOut; + for (uint i = 0; i < 4; i++) { + minAmountsOut[i] = details.tokens[i].reserveBalance.mul(ratio).div(1e18).mul(995).div(1000); // 0.5% slippage; + } + + curve.remove_liquidity(amount, minAmountsOut); + + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < 4; i++) { + + if (details.tokens[i].token == toToken) { + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + uint256 exchangeTokenAmount = details.tokens[i].token.balanceOf(address(this)); + + this.swap( + details.tokens[i].token, + toToken, + exchangeTokenAmount, + 0, + dist, + disableFlags + ); + } + + } + + function _swapToCurveSusdPoolToken( + IERC20 fromToken, + IERC20, // poolToken, + uint256 amount, + uint256[] memory distribution, + uint256 disableFlags + ) private { + uint256[] memory dist = new uint256[](distribution.length); + + PoolTokenDetails memory details = _getPoolDetails(); + + uint256[4] memory tokenAmounts; + uint256 exchangeAmountPart = amount.div(4); + for (uint i = 0; i < 4; i++) { + + if (details.tokens[i].token != fromToken) { + uint256 tokenBalanceBefore = details.tokens[i].token.balanceOf(address(this)); + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + this.swap( + fromToken, + details.tokens[i].token, + i != 3 ? exchangeAmountPart : exchangeAmountPart.add(exchangeAmountPart % 4), + 0, + dist, + disableFlags + ); + + uint256 tokenBalanceAfter = details.tokens[i].token.balanceOf(address(this)); + + tokenAmounts[i] = tokenBalanceAfter.sub(tokenBalanceBefore); + } else { + tokenAmounts[i] = i != 3 ? exchangeAmountPart : exchangeAmountPart.add(exchangeAmountPart % 4); + } + + _infiniteApproveIfNeeded(details.tokens[i].token, address(curve)); + } + + uint256 minAmount = curve.calc_token_amount(tokenAmounts, true); + + // 0.5% slippage + curve.add_liquidity(tokenAmounts, minAmount.mul(995).div(1000)); + } +} diff --git a/contracts/interface/ICurve.sol b/contracts/interface/ICurve.sol index 652e409..4fbb1de 100644 --- a/contracts/interface/ICurve.sol +++ b/contracts/interface/ICurve.sol @@ -8,3 +8,16 @@ interface ICurve { // solium-disable-next-line mixedcase function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 minDy) external; } + +interface ISusdCurve { + + function coins(int128 arg0) external view returns (address); + + function balances(int128 arg0) external view returns (uint256); + + function add_liquidity(uint256[4] calldata amounts, uint256 min_mint_amount) external; + + function remove_liquidity(uint256 _amount, uint256[4] calldata min_amounts) external; + + function calc_token_amount(uint256[4] calldata amounts, bool deposit) external view returns (uint256); +} From 0579ccdbce02649cc60a95064da3877dfb916b9e Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 27 Apr 2020 00:02:20 +0300 Subject: [PATCH 39/60] rename vars --- contracts/OneSplit.sol | 7 +++++-- contracts/OneSplitCurveSusdPoolToken.sol | 18 +++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/contracts/OneSplit.sol b/contracts/OneSplit.sol index 8995a4c..69cc7d0 100644 --- a/contracts/OneSplit.sol +++ b/contracts/OneSplit.sol @@ -13,6 +13,7 @@ import "./OneSplitAave.sol"; import "./OneSplitWeth.sol"; import "./OneSplitUniswapPoolToken.sol"; import "./OneSplitBalancerPoolToken.sol"; +import "./OneSplitCurveSusdPoolToken.sol"; //import "./OneSplitSmartToken.sol"; @@ -29,7 +30,8 @@ contract OneSplitView is OneSplitIdleView(0x23E4D1536c449e4D79E5903B4A9ddc3655be8609), OneSplitWethView, OneSplitUniswapPoolTokenView, - OneSplitBalancerPoolTokenView + OneSplitBalancerPoolTokenView, + OneSplitCurveSusdPoolTokenView //OneSplitSmartTokenView { function() external { @@ -104,7 +106,8 @@ contract OneSplit is OneSplitIdle(0x23E4D1536c449e4D79E5903B4A9ddc3655be8609), OneSplitWeth, OneSplitUniswapPoolToken, - OneSplitBalancerPoolToken + OneSplitBalancerPoolToken, + OneSplitCurveSusdPoolToken //OneSplitSmartToken { IOneSplitView public oneSplitView; diff --git a/contracts/OneSplitCurveSusdPoolToken.sol b/contracts/OneSplitCurveSusdPoolToken.sol index f9bbb01..c7b7232 100644 --- a/contracts/OneSplitCurveSusdPoolToken.sol +++ b/contracts/OneSplitCurveSusdPoolToken.sol @@ -11,22 +11,22 @@ contract OneSplitCurveSusdPoolTokenBase { ISusdCurve curve = ISusdCurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); IERC20 curveSusdToken = IERC20(0xC25a3A3b969415c80451098fa907EC722572917F); - struct TokenInfo { + struct CurveSusdTokenInfo { IERC20 token; uint256 reserveBalance; } - struct PoolTokenDetails { - TokenInfo[] tokens; + struct CurveSusdPoolTokenDetails { + CurveSusdTokenInfo[] tokens; uint256 totalSupply; } function _getPoolDetails() internal view - returns(PoolTokenDetails memory details) + returns(CurveSusdPoolTokenDetails memory details) { - details.tokens = new TokenInfo[](4); + details.tokens = new CurveSusdTokenInfo[](4); details.totalSupply = curveSusdToken.totalSupply(); for (uint256 i = 0; i < 4; i++) { details.tokens[i].token = IERC20(curve.coins(int128(i))); @@ -102,7 +102,7 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPo { distribution = new uint256[](DEXES_COUNT); - PoolTokenDetails memory details = _getPoolDetails(); + CurveSusdPoolTokenDetails memory details = _getPoolDetails(); uint256 ratio = amount.mul(1e18).div(details.totalSupply); for (uint i = 0; i < 4; i++) { @@ -146,7 +146,7 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPo { distribution = new uint256[](DEXES_COUNT); - PoolTokenDetails memory details = _getPoolDetails(); + CurveSusdPoolTokenDetails memory details = _getPoolDetails(); uint256[4] memory tokenAmounts; uint256[] memory dist; @@ -229,7 +229,7 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB uint256 disableFlags ) private { - PoolTokenDetails memory details = _getPoolDetails(); + CurveSusdPoolTokenDetails memory details = _getPoolDetails(); uint256 ratio = amount.mul(1e18).div(details.totalSupply); @@ -274,7 +274,7 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB ) private { uint256[] memory dist = new uint256[](distribution.length); - PoolTokenDetails memory details = _getPoolDetails(); + CurveSusdPoolTokenDetails memory details = _getPoolDetails(); uint256[4] memory tokenAmounts; uint256 exchangeAmountPart = amount.div(4); From e888c9aa57bc9474bcd2976d1df13d54636026a0 Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 27 Apr 2020 03:03:55 +0300 Subject: [PATCH 40/60] change snx curve address --- contracts/OneSplitBase.sol | 2 +- contracts/OneSplitCurveSusdPoolToken.sol | 16 ++++++++-------- contracts/interface/ICurve.sol | 3 --- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/contracts/OneSplitBase.sol b/contracts/OneSplitBase.sol index 6b5ba65..d05f70b 100644 --- a/contracts/OneSplitBase.sol +++ b/contracts/OneSplitBase.sol @@ -75,7 +75,7 @@ contract OneSplitRoot { ICurve public curveUsdt = ICurve(0x52EA46506B9CC5Ef470C5bf89f17Dc28bB35D85C); ICurve public curveY = ICurve(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); ICurve public curveBinance = ICurve(0x79a8C46DeA5aDa233ABaFFD40F3A0A2B1e5A4F27); - ICurve public curveSynthetix = ICurve(0x3b12e1fBb468BEa80B492d635976809Bf950186C); + ICurve public curveSynthetix = ICurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); IAaveLendingPool public aave = IAaveLendingPool(0x398eC7346DcD622eDc5ae82352F02bE94C62d119); function _getCompoundToken(IERC20 token) internal pure returns(ICompoundToken) { diff --git a/contracts/OneSplitCurveSusdPoolToken.sol b/contracts/OneSplitCurveSusdPoolToken.sol index c7b7232..ba24e6f 100644 --- a/contracts/OneSplitCurveSusdPoolToken.sol +++ b/contracts/OneSplitCurveSusdPoolToken.sol @@ -8,8 +8,8 @@ contract OneSplitCurveSusdPoolTokenBase { using SafeMath for uint256; using UniversalERC20 for IERC20; - ISusdCurve curve = ISusdCurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); IERC20 curveSusdToken = IERC20(0xC25a3A3b969415c80451098fa907EC722572917F); + ICurve public curveSynthetix = ICurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); struct CurveSusdTokenInfo { IERC20 token; @@ -29,8 +29,8 @@ contract OneSplitCurveSusdPoolTokenBase { details.tokens = new CurveSusdTokenInfo[](4); details.totalSupply = curveSusdToken.totalSupply(); for (uint256 i = 0; i < 4; i++) { - details.tokens[i].token = IERC20(curve.coins(int128(i))); - details.tokens[i].reserveBalance = curve.balances(int128(i)); + details.tokens[i].token = IERC20(curveSynthetix.coins(int128(i))); + details.tokens[i].reserveBalance = curveSynthetix.balances(int128(i)); } } @@ -170,7 +170,7 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPo } } - returnAmount = curve.calc_token_amount(tokenAmounts, true); + returnAmount = curveSynthetix.calc_token_amount(tokenAmounts, true); return (returnAmount, distribution); } @@ -238,7 +238,7 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB minAmountsOut[i] = details.tokens[i].reserveBalance.mul(ratio).div(1e18).mul(995).div(1000); // 0.5% slippage; } - curve.remove_liquidity(amount, minAmountsOut); + curveSynthetix.remove_liquidity(amount, minAmountsOut); uint256[] memory dist = new uint256[](distribution.length); for (uint i = 0; i < 4; i++) { @@ -303,12 +303,12 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB tokenAmounts[i] = i != 3 ? exchangeAmountPart : exchangeAmountPart.add(exchangeAmountPart % 4); } - _infiniteApproveIfNeeded(details.tokens[i].token, address(curve)); + _infiniteApproveIfNeeded(details.tokens[i].token, address(curveSynthetix)); } - uint256 minAmount = curve.calc_token_amount(tokenAmounts, true); + uint256 minAmount = curveSynthetix.calc_token_amount(tokenAmounts, true); // 0.5% slippage - curve.add_liquidity(tokenAmounts, minAmount.mul(995).div(1000)); + curveSynthetix.add_liquidity(tokenAmounts, minAmount.mul(995).div(1000)); } } diff --git a/contracts/interface/ICurve.sol b/contracts/interface/ICurve.sol index 4fbb1de..6df9eef 100644 --- a/contracts/interface/ICurve.sol +++ b/contracts/interface/ICurve.sol @@ -7,9 +7,6 @@ interface ICurve { // solium-disable-next-line mixedcase function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 minDy) external; -} - -interface ISusdCurve { function coins(int128 arg0) external view returns (address); From 125fc8c680b15334882316490e1cc23d1826b8c6 Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 27 Apr 2020 17:36:22 +0300 Subject: [PATCH 41/60] some changes with weights --- contracts/OneSplitCurveSusdPoolToken.sol | 55 ++++++++++++++++-------- contracts/interface/ICurve.sol | 2 + 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/contracts/OneSplitCurveSusdPoolToken.sol b/contracts/OneSplitCurveSusdPoolToken.sol index ba24e6f..e3de98d 100644 --- a/contracts/OneSplitCurveSusdPoolToken.sol +++ b/contracts/OneSplitCurveSusdPoolToken.sol @@ -9,11 +9,12 @@ contract OneSplitCurveSusdPoolTokenBase { using UniversalERC20 for IERC20; IERC20 curveSusdToken = IERC20(0xC25a3A3b969415c80451098fa907EC722572917F); - ICurve public curveSynthetix = ICurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); + ICurve public curve = ICurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); struct CurveSusdTokenInfo { IERC20 token; uint256 reserveBalance; + uint256 weightedReserveBalance; } struct CurveSusdPoolTokenDetails { @@ -29,8 +30,10 @@ contract OneSplitCurveSusdPoolTokenBase { details.tokens = new CurveSusdTokenInfo[](4); details.totalSupply = curveSusdToken.totalSupply(); for (uint256 i = 0; i < 4; i++) { - details.tokens[i].token = IERC20(curveSynthetix.coins(int128(i))); - details.tokens[i].reserveBalance = curveSynthetix.balances(int128(i)); + details.tokens[i].token = IERC20(curve.coins(int128(i))); + details.tokens[i].reserveBalance = curve.balances(int128(i)); + uint256 ratio = 1e18 * 1e18 / details.tokens[i].token.universalDecimals(); + details.tokens[i].weightedReserveBalance = ratio.mul(details.tokens[i].reserveBalance).div(1e18); } } @@ -104,9 +107,10 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPo CurveSusdPoolTokenDetails memory details = _getPoolDetails(); - uint256 ratio = amount.mul(1e18).div(details.totalSupply); for (uint i = 0; i < 4; i++) { - uint256 tokenAmountOut = details.tokens[i].reserveBalance.mul(ratio).div(1e18); + uint256 tokenAmountOut = details.tokens[i].reserveBalance + .mul(amount) + .div(details.totalSupply); if (details.tokens[i].token == toToken) { returnAmount = returnAmount.add(tokenAmountOut); @@ -147,17 +151,21 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPo distribution = new uint256[](DEXES_COUNT); CurveSusdPoolTokenDetails memory details = _getPoolDetails(); + uint256 weightedTotalSupply = details.totalSupply.mul(curve.get_virtual_price()).div(1e18); + uint256 exchangeAmountSum; uint256[4] memory tokenAmounts; uint256[] memory dist; - uint256 exchangeAmountPart = amount.div(4); for (uint i = 0; i < 4; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].weightedReserveBalance) + .div(weightedTotalSupply); if (details.tokens[i].token != fromToken) { (tokenAmounts[i], dist) = getExpectedReturn( fromToken, details.tokens[i].token, - i != 3 ? exchangeAmountPart : exchangeAmountPart.add(exchangeAmountPart % 4), + i != 3 ? exchangeAmount : amount - exchangeAmountSum, parts, disableFlags ); @@ -166,11 +174,13 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPo distribution[j] |= dist[j] << (i * 8); } } else { - tokenAmounts[i] = i != 3 ? exchangeAmountPart : exchangeAmountPart.add(exchangeAmountPart % 4); + tokenAmounts[i] = i != 3 ? exchangeAmount : amount - exchangeAmountSum; } + + exchangeAmountSum = exchangeAmountSum.add(exchangeAmount); } - returnAmount = curveSynthetix.calc_token_amount(tokenAmounts, true); + returnAmount = curve.calc_token_amount(tokenAmounts, true); return (returnAmount, distribution); } @@ -231,14 +241,15 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB CurveSusdPoolTokenDetails memory details = _getPoolDetails(); - uint256 ratio = amount.mul(1e18).div(details.totalSupply); - uint256[4] memory minAmountsOut; for (uint i = 0; i < 4; i++) { - minAmountsOut[i] = details.tokens[i].reserveBalance.mul(ratio).div(1e18).mul(995).div(1000); // 0.5% slippage; + minAmountsOut[i] = details.tokens[i].reserveBalance + .mul(amount) + .div(details.totalSupply) + .mul(995).div(1000); // 0.5% slippage; } - curveSynthetix.remove_liquidity(amount, minAmountsOut); + curve.remove_liquidity(amount, minAmountsOut); uint256[] memory dist = new uint256[](distribution.length); for (uint i = 0; i < 4; i++) { @@ -275,10 +286,14 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB uint256[] memory dist = new uint256[](distribution.length); CurveSusdPoolTokenDetails memory details = _getPoolDetails(); + uint256 weightedTotalSupply = details.totalSupply.mul(curve.get_virtual_price()).div(1e18); + uint256 exchangeAmountSum; uint256[4] memory tokenAmounts; - uint256 exchangeAmountPart = amount.div(4); for (uint i = 0; i < 4; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].weightedReserveBalance) + .div(weightedTotalSupply); if (details.tokens[i].token != fromToken) { uint256 tokenBalanceBefore = details.tokens[i].token.balanceOf(address(this)); @@ -290,7 +305,7 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB this.swap( fromToken, details.tokens[i].token, - i != 3 ? exchangeAmountPart : exchangeAmountPart.add(exchangeAmountPart % 4), + i != 3 ? exchangeAmount : amount - exchangeAmountSum, 0, dist, disableFlags @@ -300,15 +315,17 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB tokenAmounts[i] = tokenBalanceAfter.sub(tokenBalanceBefore); } else { - tokenAmounts[i] = i != 3 ? exchangeAmountPart : exchangeAmountPart.add(exchangeAmountPart % 4); + tokenAmounts[i] = i != 3 ? exchangeAmount : amount - exchangeAmountSum; } - _infiniteApproveIfNeeded(details.tokens[i].token, address(curveSynthetix)); + exchangeAmountSum = exchangeAmountSum.add(exchangeAmount); + + _infiniteApproveIfNeeded(details.tokens[i].token, address(curve)); } - uint256 minAmount = curveSynthetix.calc_token_amount(tokenAmounts, true); + uint256 minAmount = curve.calc_token_amount(tokenAmounts, true); // 0.5% slippage - curveSynthetix.add_liquidity(tokenAmounts, minAmount.mul(995).div(1000)); + curve.add_liquidity(tokenAmounts, minAmount.mul(995).div(1000)); } } diff --git a/contracts/interface/ICurve.sol b/contracts/interface/ICurve.sol index 6df9eef..f3deb7e 100644 --- a/contracts/interface/ICurve.sol +++ b/contracts/interface/ICurve.sol @@ -5,6 +5,8 @@ interface ICurve { // solium-disable-next-line mixedcase function get_dy_underlying(int128 i, int128 j, uint256 dx) external view returns(uint256 dy); + function get_virtual_price() external view returns(uint256); + // solium-disable-next-line mixedcase function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 minDy) external; From 6bc15852cfd3e2278ce23a6f5b12ac95818659b9 Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 27 Apr 2020 19:41:47 +0300 Subject: [PATCH 42/60] fix toToken split amount --- contracts/OneSplitCurveSusdPoolToken.sol | 30 +++++++++--------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/contracts/OneSplitCurveSusdPoolToken.sol b/contracts/OneSplitCurveSusdPoolToken.sol index e3de98d..4e4d52f 100644 --- a/contracts/OneSplitCurveSusdPoolToken.sol +++ b/contracts/OneSplitCurveSusdPoolToken.sol @@ -20,6 +20,7 @@ contract OneSplitCurveSusdPoolTokenBase { struct CurveSusdPoolTokenDetails { CurveSusdTokenInfo[] tokens; uint256 totalSupply; + uint256 totalWeightedBalance; } function _getPoolDetails() @@ -32,8 +33,9 @@ contract OneSplitCurveSusdPoolTokenBase { for (uint256 i = 0; i < 4; i++) { details.tokens[i].token = IERC20(curve.coins(int128(i))); details.tokens[i].reserveBalance = curve.balances(int128(i)); - uint256 ratio = 1e18 * 1e18 / details.tokens[i].token.universalDecimals(); + uint256 ratio = 1e18 * 1e18 / (10 ** details.tokens[i].token.universalDecimals()); details.tokens[i].weightedReserveBalance = ratio.mul(details.tokens[i].reserveBalance).div(1e18); + details.totalWeightedBalance = details.totalWeightedBalance.add(details.tokens[i].weightedReserveBalance); } } @@ -151,21 +153,18 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPo distribution = new uint256[](DEXES_COUNT); CurveSusdPoolTokenDetails memory details = _getPoolDetails(); - uint256 weightedTotalSupply = details.totalSupply.mul(curve.get_virtual_price()).div(1e18); - uint256 exchangeAmountSum; uint256[4] memory tokenAmounts; uint256[] memory dist; for (uint i = 0; i < 4; i++) { - uint256 exchangeAmount = amount - .mul(details.tokens[i].weightedReserveBalance) - .div(weightedTotalSupply); + uint256 ratio = details.tokens[i].weightedReserveBalance.mul(1e18).div(details.totalWeightedBalance); + uint256 exchangeAmount = amount.mul(ratio).div(1e18); if (details.tokens[i].token != fromToken) { (tokenAmounts[i], dist) = getExpectedReturn( fromToken, details.tokens[i].token, - i != 3 ? exchangeAmount : amount - exchangeAmountSum, + exchangeAmount, parts, disableFlags ); @@ -174,10 +173,8 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPo distribution[j] |= dist[j] << (i * 8); } } else { - tokenAmounts[i] = i != 3 ? exchangeAmount : amount - exchangeAmountSum; + tokenAmounts[i] = exchangeAmount; } - - exchangeAmountSum = exchangeAmountSum.add(exchangeAmount); } returnAmount = curve.calc_token_amount(tokenAmounts, true); @@ -286,14 +283,11 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB uint256[] memory dist = new uint256[](distribution.length); CurveSusdPoolTokenDetails memory details = _getPoolDetails(); - uint256 weightedTotalSupply = details.totalSupply.mul(curve.get_virtual_price()).div(1e18); - uint256 exchangeAmountSum; uint256[4] memory tokenAmounts; for (uint i = 0; i < 4; i++) { - uint256 exchangeAmount = amount - .mul(details.tokens[i].weightedReserveBalance) - .div(weightedTotalSupply); + uint256 ratio = details.tokens[i].weightedReserveBalance.mul(1e18).div(details.totalWeightedBalance); + uint256 exchangeAmount = amount.mul(ratio).div(1e18); if (details.tokens[i].token != fromToken) { uint256 tokenBalanceBefore = details.tokens[i].token.balanceOf(address(this)); @@ -305,7 +299,7 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB this.swap( fromToken, details.tokens[i].token, - i != 3 ? exchangeAmount : amount - exchangeAmountSum, + exchangeAmount, 0, dist, disableFlags @@ -315,11 +309,9 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB tokenAmounts[i] = tokenBalanceAfter.sub(tokenBalanceBefore); } else { - tokenAmounts[i] = i != 3 ? exchangeAmount : amount - exchangeAmountSum; + tokenAmounts[i] = exchangeAmount; } - exchangeAmountSum = exchangeAmountSum.add(exchangeAmount); - _infiniteApproveIfNeeded(details.tokens[i].token, address(curve)); } From 61339c06ea5fb616d42a1c515f24cd98466dfe88 Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 27 Apr 2020 19:57:22 +0300 Subject: [PATCH 43/60] gas optimizations --- contracts/OneSplitCurveSusdPoolToken.sol | 46 ++++++++++++------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/contracts/OneSplitCurveSusdPoolToken.sol b/contracts/OneSplitCurveSusdPoolToken.sol index 4e4d52f..bc328a8 100644 --- a/contracts/OneSplitCurveSusdPoolToken.sol +++ b/contracts/OneSplitCurveSusdPoolToken.sol @@ -13,13 +13,11 @@ contract OneSplitCurveSusdPoolTokenBase { struct CurveSusdTokenInfo { IERC20 token; - uint256 reserveBalance; uint256 weightedReserveBalance; } struct CurveSusdPoolTokenDetails { CurveSusdTokenInfo[] tokens; - uint256 totalSupply; uint256 totalWeightedBalance; } @@ -29,12 +27,12 @@ contract OneSplitCurveSusdPoolTokenBase { returns(CurveSusdPoolTokenDetails memory details) { details.tokens = new CurveSusdTokenInfo[](4); - details.totalSupply = curveSusdToken.totalSupply(); for (uint256 i = 0; i < 4; i++) { details.tokens[i].token = IERC20(curve.coins(int128(i))); - details.tokens[i].reserveBalance = curve.balances(int128(i)); - uint256 ratio = 1e18 * 1e18 / (10 ** details.tokens[i].token.universalDecimals()); - details.tokens[i].weightedReserveBalance = ratio.mul(details.tokens[i].reserveBalance).div(1e18); + details.tokens[i].weightedReserveBalance = uint256(1e36) + .div(10 ** details.tokens[i].token.universalDecimals()) + .mul(curve.balances(int128(i))) + .div(1e18); details.totalWeightedBalance = details.totalWeightedBalance.add(details.tokens[i].weightedReserveBalance); } } @@ -93,7 +91,7 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPo } function _getExpectedReturnFromCurveSusdPoolToken( - IERC20, // poolToken + IERC20 poolToken, IERC20 toToken, uint256 amount, uint256 parts, @@ -107,20 +105,21 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPo { distribution = new uint256[](DEXES_COUNT); - CurveSusdPoolTokenDetails memory details = _getPoolDetails(); - + uint256 totalSupply = poolToken.totalSupply(); for (uint i = 0; i < 4; i++) { - uint256 tokenAmountOut = details.tokens[i].reserveBalance + IERC20 coin = IERC20(curve.coins(int128(i))); + + uint256 tokenAmountOut = curve.balances(int128(i)) .mul(amount) - .div(details.totalSupply); + .div(totalSupply); - if (details.tokens[i].token == toToken) { + if (coin == toToken) { returnAmount = returnAmount.add(tokenAmountOut); continue; } (uint256 ret, uint256[] memory dist) = getExpectedReturn( - details.tokens[i].token, + coin, toToken, tokenAmountOut, parts, @@ -229,20 +228,19 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB } function _swapFromCurveSusdPoolToken( - IERC20, // poolToken + IERC20 poolToken, IERC20 toToken, uint256 amount, uint256[] memory distribution, uint256 disableFlags ) private { - CurveSusdPoolTokenDetails memory details = _getPoolDetails(); - + uint256 totalSupply = poolToken.totalSupply(); uint256[4] memory minAmountsOut; for (uint i = 0; i < 4; i++) { - minAmountsOut[i] = details.tokens[i].reserveBalance + minAmountsOut[i] = curve.balances(int128(i)) .mul(amount) - .div(details.totalSupply) + .div(totalSupply) .mul(995).div(1000); // 0.5% slippage; } @@ -250,8 +248,9 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB uint256[] memory dist = new uint256[](distribution.length); for (uint i = 0; i < 4; i++) { + IERC20 coin = IERC20(curve.coins(int128(i))); - if (details.tokens[i].token == toToken) { + if (coin == toToken) { continue; } @@ -259,10 +258,10 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB dist[j] = (distribution[j] >> (i * 8)) & 0xFF; } - uint256 exchangeTokenAmount = details.tokens[i].token.balanceOf(address(this)); + uint256 exchangeTokenAmount = coin.balanceOf(address(this)); this.swap( - details.tokens[i].token, + coin, toToken, exchangeTokenAmount, 0, @@ -286,8 +285,9 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB uint256[4] memory tokenAmounts; for (uint i = 0; i < 4; i++) { - uint256 ratio = details.tokens[i].weightedReserveBalance.mul(1e18).div(details.totalWeightedBalance); - uint256 exchangeAmount = amount.mul(ratio).div(1e18); + uint256 exchangeAmount = amount.mul( + details.tokens[i].weightedReserveBalance.mul(1e18).div(details.totalWeightedBalance) + ).div(1e18); if (details.tokens[i].token != fromToken) { uint256 tokenBalanceBefore = details.tokens[i].token.balanceOf(address(this)); From 8d0ed0f979e2e0712ee3427eaa2ffad275cafda0 Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 27 Apr 2020 21:54:41 +0300 Subject: [PATCH 44/60] fix curveSynthetix --- contracts/OneSplitBase.sol | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/contracts/OneSplitBase.sol b/contracts/OneSplitBase.sol index d05f70b..af9e175 100644 --- a/contracts/OneSplitBase.sol +++ b/contracts/OneSplitBase.sol @@ -338,21 +338,15 @@ contract OneSplitBaseView is IOneSplitView, OneSplitRoot { int128 i = (fromToken == dai ? 1 : 0) + (fromToken == usdc ? 2 : 0) + (fromToken == usdt ? 3 : 0) + - (fromToken == tusd ? 4 : 0) + - (fromToken == susd ? 5 : 0); + (fromToken == susd ? 4 : 0); int128 j = (destToken == dai ? 1 : 0) + (destToken == usdc ? 2 : 0) + (destToken == usdt ? 3 : 0) + - (destToken == tusd ? 4 : 0) + - (destToken == susd ? 5 : 0); + (destToken == susd ? 4 : 0); if (i == 0 || j == 0) { return 0; } - if (fromToken != susd && destToken != susd) { - return 0; - } - return curveSynthetix.get_dy_underlying(i - 1, j - 1, amount); } @@ -800,21 +794,15 @@ contract OneSplitBase is IOneSplit, OneSplitRoot { int128 i = (fromToken == dai ? 1 : 0) + (fromToken == usdc ? 2 : 0) + (fromToken == usdt ? 3 : 0) + - (fromToken == tusd ? 4 : 0) + - (fromToken == susd ? 5 : 0); + (fromToken == susd ? 4 : 0); int128 j = (destToken == dai ? 1 : 0) + (destToken == usdc ? 2 : 0) + (destToken == usdt ? 3 : 0) + - (destToken == tusd ? 4 : 0) + - (destToken == susd ? 5 : 0); + (destToken == susd ? 4 : 0); if (i == 0 || j == 0) { return 0; } - if (fromToken != susd && destToken != susd) { - return 0; - } - _infiniteApproveIfNeeded(fromToken, address(curveSynthetix)); curveSynthetix.exchange_underlying(i - 1, j - 1, amount, 0); } From af8a4df4de42371b5430e821f753cba78c4466b6 Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 27 Apr 2020 22:02:59 +0300 Subject: [PATCH 45/60] optimize --- contracts/OneSplitCurveSusdPoolToken.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/OneSplitCurveSusdPoolToken.sol b/contracts/OneSplitCurveSusdPoolToken.sol index bc328a8..791b74c 100644 --- a/contracts/OneSplitCurveSusdPoolToken.sol +++ b/contracts/OneSplitCurveSusdPoolToken.sol @@ -8,8 +8,8 @@ contract OneSplitCurveSusdPoolTokenBase { using SafeMath for uint256; using UniversalERC20 for IERC20; - IERC20 curveSusdToken = IERC20(0xC25a3A3b969415c80451098fa907EC722572917F); - ICurve public curve = ICurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); + IERC20 constant curveSusdToken = IERC20(0xC25a3A3b969415c80451098fa907EC722572917F); + ICurve constant curve = ICurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); struct CurveSusdTokenInfo { IERC20 token; From f026063e49bcc876fd5d6f9efa18bd93d018aae0 Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 27 Apr 2020 23:05:23 +0300 Subject: [PATCH 46/60] Anton's refactoring --- contracts/OneSplitCurveSusdPoolToken.sol | 99 +++++++++++------------- 1 file changed, 46 insertions(+), 53 deletions(-) diff --git a/contracts/OneSplitCurveSusdPoolToken.sol b/contracts/OneSplitCurveSusdPoolToken.sol index 791b74c..8533fe0 100644 --- a/contracts/OneSplitCurveSusdPoolToken.sol +++ b/contracts/OneSplitCurveSusdPoolToken.sol @@ -29,18 +29,17 @@ contract OneSplitCurveSusdPoolTokenBase { details.tokens = new CurveSusdTokenInfo[](4); for (uint256 i = 0; i < 4; i++) { details.tokens[i].token = IERC20(curve.coins(int128(i))); - details.tokens[i].weightedReserveBalance = uint256(1e36) - .div(10 ** details.tokens[i].token.universalDecimals()) - .mul(curve.balances(int128(i))) - .div(1e18); - details.totalWeightedBalance = details.totalWeightedBalance.add(details.tokens[i].weightedReserveBalance); + details.tokens[i].weightedReserveBalance = curve.balances(int128(i)) + .mul(1e18).div(10 ** details.tokens[i].token.universalDecimals()); + details.totalWeightedBalance = details.totalWeightedBalance.add( + details.tokens[i].weightedReserveBalance + ); } } - } -contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPoolTokenBase { +contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPoolTokenBase { function getExpectedReturn( IERC20 fromToken, IERC20 toToken, @@ -118,7 +117,7 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPo continue; } - (uint256 ret, uint256[] memory dist) = getExpectedReturn( + (uint256 ret, uint256[] memory dist) = this.getExpectedReturn( coin, toToken, tokenAmountOut, @@ -156,23 +155,25 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPo uint256[4] memory tokenAmounts; uint256[] memory dist; for (uint i = 0; i < 4; i++) { - uint256 ratio = details.tokens[i].weightedReserveBalance.mul(1e18).div(details.totalWeightedBalance); - uint256 exchangeAmount = amount.mul(ratio).div(1e18); - - if (details.tokens[i].token != fromToken) { - (tokenAmounts[i], dist) = getExpectedReturn( - fromToken, - details.tokens[i].token, - exchangeAmount, - parts, - disableFlags - ); + uint256 exchangeAmount = amount + .mul(details.tokens[i].weightedReserveBalance) + .div(details.totalWeightedBalance); - for (uint j = 0; j < distribution.length; j++) { - distribution[j] |= dist[j] << (i * 8); - } - } else { + if (details.tokens[i].token == fromToken) { tokenAmounts[i] = exchangeAmount; + continue; + } + + (tokenAmounts[i], dist) = this.getExpectedReturn( + fromToken, + details.tokens[i].token, + exchangeAmount, + parts, + disableFlags + ); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); } } @@ -180,7 +181,6 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitBaseView, OneSplitCurveSusdPo return (returnAmount, distribution); } - } @@ -258,7 +258,7 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB dist[j] = (distribution[j] >> (i * 8)) & 0xFF; } - uint256 exchangeTokenAmount = coin.balanceOf(address(this)); + uint256 exchangeTokenAmount = coin.universalBalanceOf(address(this)); this.swap( coin, @@ -269,7 +269,6 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB disableFlags ); } - } function _swapToCurveSusdPoolToken( @@ -285,39 +284,33 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB uint256[4] memory tokenAmounts; for (uint i = 0; i < 4; i++) { - uint256 exchangeAmount = amount.mul( - details.tokens[i].weightedReserveBalance.mul(1e18).div(details.totalWeightedBalance) - ).div(1e18); + uint256 exchangeAmount = amount + .mul(details.tokens[i].weightedReserveBalance) + .div(details.totalWeightedBalance); - if (details.tokens[i].token != fromToken) { - uint256 tokenBalanceBefore = details.tokens[i].token.balanceOf(address(this)); - - for (uint j = 0; j < distribution.length; j++) { - dist[j] = (distribution[j] >> (i * 8)) & 0xFF; - } - - this.swap( - fromToken, - details.tokens[i].token, - exchangeAmount, - 0, - dist, - disableFlags - ); - - uint256 tokenBalanceAfter = details.tokens[i].token.balanceOf(address(this)); + _infiniteApproveIfNeeded(details.tokens[i].token, address(curve)); - tokenAmounts[i] = tokenBalanceAfter.sub(tokenBalanceBefore); - } else { + if (details.tokens[i].token == fromToken) { tokenAmounts[i] = exchangeAmount; + continue; } - _infiniteApproveIfNeeded(details.tokens[i].token, address(curve)); - } + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } - uint256 minAmount = curve.calc_token_amount(tokenAmounts, true); + this.swap( + fromToken, + details.tokens[i].token, + exchangeAmount, + 0, + dist, + disableFlags + ); + + tokenAmounts[i] = details.tokens[i].token.univeralBalanceOf(address(this)); + } - // 0.5% slippage - curve.add_liquidity(tokenAmounts, minAmount.mul(995).div(1000)); + curve.add_liquidity(tokenAmounts, 0); } } From 1dc5c723b8b2d4180336d1fa32c90c64bc2f1db9 Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 27 Apr 2020 23:35:27 +0300 Subject: [PATCH 47/60] fix --- contracts/OneSplitCurveSusdPoolToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/OneSplitCurveSusdPoolToken.sol b/contracts/OneSplitCurveSusdPoolToken.sol index 8533fe0..b2bb585 100644 --- a/contracts/OneSplitCurveSusdPoolToken.sol +++ b/contracts/OneSplitCurveSusdPoolToken.sol @@ -308,7 +308,7 @@ contract OneSplitCurveSusdPoolToken is OneSplitBase, OneSplitCurveSusdPoolTokenB disableFlags ); - tokenAmounts[i] = details.tokens[i].token.univeralBalanceOf(address(this)); + tokenAmounts[i] = details.tokens[i].token.universalBalanceOf(address(this)); } curve.add_liquidity(tokenAmounts, 0); From f69e44dd0aa79163b0d66a1e8c91e2c74505bd46 Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 2 May 2020 19:35:46 +0300 Subject: [PATCH 48/60] remove smart token tests --- test/OneSplit.js | 37 ++----------------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/test/OneSplit.js b/test/OneSplit.js index 308cc95..af95038 100644 --- a/test/OneSplit.js +++ b/test/OneSplit.js @@ -1,5 +1,5 @@ -const { expectRevert } = require('@openzeppelin/test-helpers'); -const { expect } = require('chai'); +// const { expectRevert } = require('@openzeppelin/test-helpers'); +// const { expect } = require('chai'); const assert = require('assert'); const OneSplitView = artifacts.require('OneSplitView'); @@ -9,39 +9,6 @@ const OneSplitWrap = artifacts.require('OneSplitWrap'); contract('OneSplit', function ([_, addr1]) { - describe('OneSplitSmartContract', async function () { - beforeEach('should be ok', async function () { - this.smartTokenView = await OneSplitViewMock.new(); - }); - - it('should view buying price', async function () { - const res = await this.smartTokenView.getExpectedReturn( - '0x0000000000000000000000000000000000000000', // ETH - '0x482c31355F4f7966fFcD38eC5c9635ACAe5F4D4F', // Ether Token Smart Relay Token (ETHUSDB) - '0x' + Number(web3.utils.toWei('0.5')).toString(16), - '0x' + (10).toString(16), - '0x0', - ); - - console.log(res['0'].toString()); - console.log(res['1'].map(x => x.toString())); - }); - - it('should view selling price', async function () { - const res = await this.smartTokenView.getExpectedReturn( - '0x482c31355F4f7966fFcD38eC5c9635ACAe5F4D4F', // Ether Token Smart Relay Token (ETHUSDB) - '0x0000000000000000000000000000000000000000', // ETH - '0x' + Number(web3.utils.toWei('20')).toString(16), - '0x' + (10).toString(16), - '0x0' - ); - - console.log(res['0'].toString()); - console.log(res['1'].map(x => x.toString())); - }); - - }); - describe('OneSplit', async function () { beforeEach('should be ok', async function () { const subSplitView = await OneSplitView.new(); From c8940ef20411cd953a4d1028fb9537bd269dad65 Mon Sep 17 00:00:00 2001 From: Kirill Date: Sat, 2 May 2020 19:55:42 +0300 Subject: [PATCH 49/60] some fixes --- contracts/OneSplit.sol | 4 ++-- contracts/OneSplitBalancerPoolToken.sol | 28 ++++++++++++------------- contracts/OneSplitSmartToken.sol | 6 +++--- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/contracts/OneSplit.sol b/contracts/OneSplit.sol index 5386118..8b6ed04 100644 --- a/contracts/OneSplit.sol +++ b/contracts/OneSplit.sol @@ -30,7 +30,7 @@ contract OneSplitViewWrap is OneSplitWethView, OneSplitBalancerPoolTokenView, OneSplitUniswapPoolTokenView, - OneSplitCurveSusdPoolTokenView + OneSplitCurveSusdPoolTokenView, OneSplitSmartTokenView { IOneSplitView public oneSplitView; @@ -104,7 +104,7 @@ contract OneSplitWrap is OneSplitWeth, OneSplitBalancerPoolToken, OneSplitUniswapPoolToken, - OneSplitCurveSusdPoolToken + OneSplitCurveSusdPoolToken, OneSplitSmartToken { IOneSplitView public oneSplitView; diff --git a/contracts/OneSplitBalancerPoolToken.sol b/contracts/OneSplitBalancerPoolToken.sol index 3f91a5c..88f2a25 100644 --- a/contracts/OneSplitBalancerPoolToken.sol +++ b/contracts/OneSplitBalancerPoolToken.sol @@ -49,7 +49,7 @@ contract OneSplitBalancerPoolTokenView is OneSplitViewWrapBase, OneSplitBalancer IERC20 toToken, uint256 amount, uint256 parts, - uint256 disableFlags + uint256 flags ) public view @@ -63,7 +63,7 @@ contract OneSplitBalancerPoolTokenView is OneSplitViewWrapBase, OneSplitBalancer } - if (!disableFlags.check(FLAG_DISABLE_BALANCER_POOL_TOKEN)) { + if (!flags.check(FLAG_DISABLE_BALANCER_POOL_TOKEN)) { bool isPoolTokenFrom = bFactory.isBPool(address(fromToken)); bool isPoolTokenTo = bFactory.isBPool(address(toToken)); @@ -123,7 +123,7 @@ contract OneSplitBalancerPoolTokenView is OneSplitViewWrapBase, OneSplitBalancer toToken, amount, parts, - disableFlags + flags ); } @@ -132,7 +132,7 @@ contract OneSplitBalancerPoolTokenView is OneSplitViewWrapBase, OneSplitBalancer IERC20 toToken, uint256 amount, uint256 parts, - uint256 disableFlags + uint256 flags ) private view @@ -163,7 +163,7 @@ contract OneSplitBalancerPoolTokenView is OneSplitViewWrapBase, OneSplitBalancer toToken, tokenAmountOut, parts, - disableFlags + flags ); returnAmount = returnAmount.add(ret); @@ -181,7 +181,7 @@ contract OneSplitBalancerPoolTokenView is OneSplitViewWrapBase, OneSplitBalancer IERC20 poolToken, uint256 amount, uint256 parts, - uint256 disableFlags + uint256 flags ) private view @@ -210,7 +210,7 @@ contract OneSplitBalancerPoolTokenView is OneSplitViewWrapBase, OneSplitBalancer details.tokens[i].token, exchangeAmount, parts, - disableFlags + flags ); for (uint j = 0; j < distribution.length; j++) { @@ -265,13 +265,13 @@ contract OneSplitBalancerPoolToken is OneSplitBaseWrap, OneSplitBalancerPoolToke IERC20 toToken, uint256 amount, uint256[] memory distribution, - uint256 disableFlags + uint256 flags ) internal { if (fromToken == toToken) { return; } - if (!disableFlags.check(FLAG_DISABLE_BALANCER_POOL_TOKEN)) { + if (!flags.check(FLAG_DISABLE_BALANCER_POOL_TOKEN)) { bool isPoolTokenFrom = bFactory.isBPool(address(fromToken)); bool isPoolTokenTo = bFactory.isBPool(address(toToken)); @@ -332,7 +332,7 @@ contract OneSplitBalancerPoolToken is OneSplitBaseWrap, OneSplitBalancerPoolToke toToken, amount, distribution, - disableFlags + flags ); } @@ -341,7 +341,7 @@ contract OneSplitBalancerPoolToken is OneSplitBaseWrap, OneSplitBalancerPoolToke IERC20 toToken, uint256 amount, uint256[] memory distribution, - uint256 disableFlags + uint256 flags ) private { IBPool bToken = IBPool(address(poolToken)); @@ -378,7 +378,7 @@ contract OneSplitBalancerPoolToken is OneSplitBaseWrap, OneSplitBalancerPoolToke exchangeTokenAmount, 0, dist, - disableFlags + flags ); } @@ -389,7 +389,7 @@ contract OneSplitBalancerPoolToken is OneSplitBaseWrap, OneSplitBalancerPoolToke IERC20 poolToken, uint256 amount, uint256[] memory distribution, - uint256 disableFlags + uint256 flags ) private { uint256[] memory dist = new uint256[](distribution.length); uint256 minFundAmount = uint256(-1); @@ -416,7 +416,7 @@ contract OneSplitBalancerPoolToken is OneSplitBaseWrap, OneSplitBalancerPoolToke exchangeAmount, 0, dist, - disableFlags + flags ); uint256 tokenBalanceAfter = details.tokens[i].token.balanceOf(address(this)); diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 5240a02..27a91ff 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -94,7 +94,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase return (amount, new uint256[](DEXES_COUNT)); } - if (!disableFlags.check(FLAG_DISABLE_SMART_TOKEN)) { + if (!flags.check(FLAG_DISABLE_SMART_TOKEN)) { bool isSmartTokenFrom = smartTokenRegistry.isSmartToken(fromToken); bool isSmartTokenTo = smartTokenRegistry.isSmartToken(toToken); @@ -173,7 +173,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase uint256[] memory distribution ) { - distribution = new uint256[](9); + distribution = new uint256[](DEXES_COUNT); SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); @@ -221,7 +221,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase uint256[] memory distribution ) { - distribution = new uint256[](9); + distribution = new uint256[](DEXES_COUNT); minFundAmount = uint256(-1); SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); From e98b78169fcc2c7d609abb5b445a17193bea2a5a Mon Sep 17 00:00:00 2001 From: Anton Bukov Date: Sat, 2 May 2020 23:03:05 +0300 Subject: [PATCH 50/60] Fix --- contracts/OneSplit.sol | 36 ++++++++++ contracts/OneSplitBase.sol | 80 ++++++++++++++-------- contracts/OneSplitCurveSusdPoolToken.sol | 28 ++++---- contracts/OneSplitSmartToken.sol | 35 +++++----- contracts/OneSplitUniswapPoolToken.sol | 36 +++++----- contracts/interface/ISmartTokenFormula.sol | 4 +- 6 files changed, 137 insertions(+), 82 deletions(-) diff --git a/contracts/OneSplit.sol b/contracts/OneSplit.sol index 8b6ed04..28355ce 100644 --- a/contracts/OneSplit.sol +++ b/contracts/OneSplit.sol @@ -88,6 +88,20 @@ contract OneSplitViewWrap is flags ); } + + function _calculateBancorReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 flags + ) public view returns(uint256) { + return oneSplitView._calculateBancorReturn( + fromToken, + toToken, + amount, + flags + ); + } } @@ -191,4 +205,26 @@ contract OneSplitWrap is case 0 { revert(add(data, 32), returndatasize) } } } + + function _swapOnBancorSafe( + IERC20 fromToken, + IERC20 toToken, + uint256 amount + ) external returns(uint256) { + (bool success, bytes memory data) = address(oneSplit).delegatecall( + abi.encodeWithSelector( + this._swapOnBancorSafe.selector, + fromToken, + toToken, + amount + ) + ); + + assembly { + switch success + // delegatecall returns 0 on error. + case 0 { revert(add(data, 32), returndatasize) } + case 1 { return(add(data, 32), returndatasize) } + } + } } diff --git a/contracts/OneSplitBase.sol b/contracts/OneSplitBase.sol index 9458357..4a1353d 100644 --- a/contracts/OneSplitBase.sol +++ b/contracts/OneSplitBase.sol @@ -36,6 +36,13 @@ contract IOneSplitView is IOneSplitConsts { uint256 returnAmount, uint256[] memory distribution ); + + function _calculateBancorReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 flags + ) external view returns(uint256); } @@ -294,6 +301,13 @@ contract OneSplitViewWrapBase is IOneSplitView, OneSplitRoot { uint256 returnAmount, uint256[] memory distribution ); + + function _calculateBancorReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 flags + ) public view returns(uint256); } @@ -322,18 +336,18 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { } function(IERC20,IERC20,uint256,uint256) view returns(uint256)[DEXES_COUNT] memory reserves = [ - flags.check(FLAG_DISABLE_UNISWAP) ? _calculateNoReturn : calculateUniswapReturn, - flags.check(FLAG_DISABLE_KYBER) ? _calculateNoReturn : calculateKyberReturn, - flags.check(FLAG_DISABLE_BANCOR) ? _calculateNoReturn : calculateBancorReturn, - flags.check(FLAG_DISABLE_OASIS) ? _calculateNoReturn : calculateOasisReturn, - flags.check(FLAG_DISABLE_CURVE_COMPOUND) ? _calculateNoReturn : calculateCurveCompound, - flags.check(FLAG_DISABLE_CURVE_USDT) ? _calculateNoReturn : calculateCurveUsdt, - flags.check(FLAG_DISABLE_CURVE_Y) ? _calculateNoReturn : calculateCurveY, - flags.check(FLAG_DISABLE_CURVE_BINANCE) ? _calculateNoReturn : calculateCurveBinance, - flags.check(FLAG_DISABLE_CURVE_SYNTHETIX) ? _calculateNoReturn : calculateCurveSynthetix, - !flags.check(FLAG_ENABLE_UNISWAP_COMPOUND) ? _calculateNoReturn : calculateUniswapCompound, - !flags.check(FLAG_ENABLE_UNISWAP_CHAI) ? _calculateNoReturn : calculateUniswapChai, - !flags.check(FLAG_ENABLE_UNISWAP_AAVE) ? _calculateNoReturn : calculateUniswapAave + flags.check(FLAG_DISABLE_UNISWAP) ? _calculateNoReturn : _calculateUniswapReturn, + flags.check(FLAG_DISABLE_KYBER) ? _calculateNoReturn : _calculateKyberReturn, + flags.check(FLAG_DISABLE_BANCOR) ? _calculateNoReturn : _calculateBancorReturn, + flags.check(FLAG_DISABLE_OASIS) ? _calculateNoReturn : _calculateOasisReturn, + flags.check(FLAG_DISABLE_CURVE_COMPOUND) ? _calculateNoReturn : _calculateCurveCompound, + flags.check(FLAG_DISABLE_CURVE_USDT) ? _calculateNoReturn : _calculateCurveUsdt, + flags.check(FLAG_DISABLE_CURVE_Y) ? _calculateNoReturn : _calculateCurveY, + flags.check(FLAG_DISABLE_CURVE_BINANCE) ? _calculateNoReturn : _calculateCurveBinance, + flags.check(FLAG_DISABLE_CURVE_SYNTHETIX) ? _calculateNoReturn : _calculateCurveSynthetix, + !flags.check(FLAG_ENABLE_UNISWAP_COMPOUND) ? _calculateNoReturn : _calculateUniswapCompound, + !flags.check(FLAG_ENABLE_UNISWAP_CHAI) ? _calculateNoReturn : _calculateUniswapChai, + !flags.check(FLAG_ENABLE_UNISWAP_AAVE) ? _calculateNoReturn : _calculateUniswapAave ]; uint256[DEXES_COUNT] memory rates; @@ -381,7 +395,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { // View Helpers - function calculateCurveCompound( + function _calculateCurveCompound( IERC20 fromToken, IERC20 destToken, uint256 amount, @@ -396,7 +410,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return curveCompound.get_dy_underlying(i - 1, j - 1, amount); } - function calculateCurveUsdt( + function _calculateCurveUsdt( IERC20 fromToken, IERC20 destToken, uint256 amount, @@ -415,7 +429,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return curveUsdt.get_dy_underlying(i - 1, j - 1, amount); } - function calculateCurveY( + function _calculateCurveY( IERC20 fromToken, IERC20 destToken, uint256 amount, @@ -436,7 +450,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return curveY.get_dy_underlying(i - 1, j - 1, amount); } - function calculateCurveBinance( + function _calculateCurveBinance( IERC20 fromToken, IERC20 destToken, uint256 amount, @@ -457,7 +471,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return curveBinance.get_dy_underlying(i - 1, j - 1, amount); } - function calculateCurveSynthetix( + function _calculateCurveSynthetix( IERC20 fromToken, IERC20 destToken, uint256 amount, @@ -478,7 +492,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return curveSynthetix.get_dy_underlying(i - 1, j - 1, amount); } - function calculateUniswapReturn( + function _calculateUniswapReturn( IERC20 fromToken, IERC20 toToken, uint256 amount, @@ -527,7 +541,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return returnAmount; } - function calculateUniswapCompound( + function _calculateUniswapCompound( IERC20 fromToken, IERC20 toToken, uint256 amount, @@ -540,7 +554,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { if (!fromToken.isETH()) { ICompoundToken fromCompound = _getCompoundToken(fromToken); if (fromCompound != ICompoundToken(0)) { - return calculateUniswapReturn( + return _calculateUniswapReturn( fromCompound, toToken, amount.mul(1e18).div(fromCompound.exchangeRateStored()), @@ -550,7 +564,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { } else { ICompoundToken toCompound = _getCompoundToken(toToken); if (toCompound != ICompoundToken(0)) { - return calculateUniswapReturn( + return _calculateUniswapReturn( fromToken, toCompound, amount, @@ -562,14 +576,14 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return 0; } - function calculateUniswapChai( + function _calculateUniswapChai( IERC20 fromToken, IERC20 toToken, uint256 amount, uint256 flags ) public view returns(uint256) { if (fromToken == dai && toToken.isETH()) { - return calculateUniswapReturn( + return _calculateUniswapReturn( chai, toToken, chai.daiToChai(amount), @@ -578,7 +592,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { } if (fromToken.isETH() && toToken == dai) { - return chai.chaiToDai(calculateUniswapReturn( + return chai.chaiToDai(_calculateUniswapReturn( fromToken, chai, amount, @@ -589,7 +603,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return 0; } - function calculateUniswapAave( + function _calculateUniswapAave( IERC20 fromToken, IERC20 toToken, uint256 amount, @@ -602,7 +616,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { if (!fromToken.isETH()) { IAaveToken fromAave = _getAaveToken(fromToken); if (fromAave != IAaveToken(0)) { - return calculateUniswapReturn( + return _calculateUniswapReturn( fromAave, toToken, amount, @@ -612,7 +626,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { } else { IAaveToken toAave = _getAaveToken(toToken); if (toAave != IAaveToken(0)) { - return calculateUniswapReturn( + return _calculateUniswapReturn( fromToken, toAave, amount, @@ -624,7 +638,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return 0; } - function calculateKyberReturn( + function _calculateKyberReturn( IERC20 fromToken, IERC20 toToken, uint256 amount, @@ -717,7 +731,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { .div(1e18); } - function calculateBancorReturn( + function _calculateBancorReturn( IERC20 fromToken, IERC20 toToken, uint256 amount, @@ -741,7 +755,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return returnAmount; } - function calculateOasisReturn( + function _calculateOasisReturn( IERC20 fromToken, IERC20 toToken, uint256 amount, @@ -801,6 +815,12 @@ contract OneSplitBaseWrap is IOneSplit, OneSplitRoot { uint256[] memory distribution, uint256 /*flags*/ // See constants in IOneSplit.sol ) internal; + + function _swapOnBancorSafe( + IERC20 fromToken, + IERC20 toToken, + uint256 amount + ) external returns(uint256); } diff --git a/contracts/OneSplitCurveSusdPoolToken.sol b/contracts/OneSplitCurveSusdPoolToken.sol index 72ab979..a2fd7bb 100644 --- a/contracts/OneSplitCurveSusdPoolToken.sol +++ b/contracts/OneSplitCurveSusdPoolToken.sol @@ -45,7 +45,7 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitViewWrapBase, OneSplitCurveSu IERC20 toToken, uint256 amount, uint256 parts, - uint256 disableFlags + uint256 flags ) public view @@ -59,7 +59,7 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitViewWrapBase, OneSplitCurveSu } - if (!disableFlags.check(FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN)) { + if (!flags.check(FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN)) { if (fromToken == curveSusdToken) { return _getExpectedReturnFromCurveSusdPoolToken( fromToken, @@ -86,7 +86,7 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitViewWrapBase, OneSplitCurveSu toToken, amount, parts, - disableFlags + flags ); } @@ -95,7 +95,7 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitViewWrapBase, OneSplitCurveSu IERC20 toToken, uint256 amount, uint256 parts, - uint256 disableFlags + uint256 flags ) private view @@ -124,7 +124,7 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitViewWrapBase, OneSplitCurveSu toToken, tokenAmountOut, parts, - disableFlags + flags ); returnAmount = returnAmount.add(ret); @@ -142,7 +142,7 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitViewWrapBase, OneSplitCurveSu IERC20, // poolToken uint256 amount, uint256 parts, - uint256 disableFlags + uint256 flags ) private view @@ -172,7 +172,7 @@ contract OneSplitCurveSusdPoolTokenView is OneSplitViewWrapBase, OneSplitCurveSu details.tokens[i].token, exchangeAmount, parts, - disableFlags + flags ); for (uint j = 0; j < distribution.length; j++) { @@ -193,13 +193,13 @@ contract OneSplitCurveSusdPoolToken is OneSplitBaseWrap, OneSplitCurveSusdPoolTo IERC20 toToken, uint256 amount, uint256[] memory distribution, - uint256 disableFlags + uint256 flags ) internal { if (fromToken == toToken) { return; } - if (!disableFlags.check(FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN)) { + if (!flags.check(FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN)) { if (fromToken == curveSusdToken) { return _swapFromCurveSusdPoolToken( fromToken, @@ -226,7 +226,7 @@ contract OneSplitCurveSusdPoolToken is OneSplitBaseWrap, OneSplitCurveSusdPoolTo toToken, amount, distribution, - disableFlags + flags ); } @@ -235,7 +235,7 @@ contract OneSplitCurveSusdPoolToken is OneSplitBaseWrap, OneSplitCurveSusdPoolTo IERC20 toToken, uint256 amount, uint256[] memory distribution, - uint256 disableFlags + uint256 flags ) private { uint256 totalSupply = poolToken.totalSupply(); @@ -269,7 +269,7 @@ contract OneSplitCurveSusdPoolToken is OneSplitBaseWrap, OneSplitCurveSusdPoolTo exchangeTokenAmount, 0, dist, - disableFlags + flags ); } } @@ -279,7 +279,7 @@ contract OneSplitCurveSusdPoolToken is OneSplitBaseWrap, OneSplitCurveSusdPoolTo IERC20, // poolToken, uint256 amount, uint256[] memory distribution, - uint256 disableFlags + uint256 flags ) private { uint256[] memory dist = new uint256[](distribution.length); @@ -308,7 +308,7 @@ contract OneSplitCurveSusdPoolToken is OneSplitBaseWrap, OneSplitCurveSusdPoolTo exchangeAmount, 0, dist, - disableFlags + flags ); tokenAmounts[i] = details.tokens[i].token.universalBalanceOf(address(this)); diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 27a91ff..91a9599 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -146,7 +146,6 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase parts, 0 ); - } } @@ -164,7 +163,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase IERC20 toToken, uint256 amount, uint256 parts, - uint256 disableFlags + uint256 flags ) private view @@ -178,7 +177,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); for (uint i = 0; i < details.tokens.length; i++) { - uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( + uint256 srcAmount = smartTokenFormula._calculateLiquidateReturn( smartToken.totalSupply(), _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), uint32(details.totalRatio), @@ -195,7 +194,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase toToken, srcAmount, parts, - disableFlags + flags ); returnAmount = returnAmount.add(ret); @@ -212,7 +211,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase IERC20 smartToken, uint256 amount, uint256 parts, - uint256 disableFlags + uint256 flags ) private view @@ -241,7 +240,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase _canonicalSUSD(details.tokens[i].token), exchangeAmount, parts, - disableFlags + flags ); for (uint j = 0; j < distribution.length; j++) { @@ -251,7 +250,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase tokenAmounts[i] = exchangeAmount; } - fundAmounts[i] = smartTokenFormula.calculatePurchaseReturn( + fundAmounts[i] = smartTokenFormula._calculatePurchaseReturn( smartToken.totalSupply(), _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), uint32(details.totalRatio), @@ -273,7 +272,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase } uint256 leftover = tokenAmounts[i].sub( - smartTokenFormula.calculateLiquidateReturn( + smartTokenFormula._calculateLiquidateReturn( _smartToken.totalSupply().add(_minFundAmount), _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter).add(tokenAmounts[i]), uint32(details.totalRatio), @@ -281,11 +280,11 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase ) ); - uint256 tokenRet = calculateBancorReturn( + uint256 tokenRet = _calculateBancorReturn( _canonicalSUSD(details.tokens[i].token), _smartToken, leftover, - disableFlags + flags ); minFundAmount = minFundAmount.add(tokenRet); @@ -309,7 +308,7 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { return; } - if (!disableFlags.check(FLAG_DISABLE_SMART_TOKEN)) { + if (!flags.check(FLAG_DISABLE_SMART_TOKEN)) { bool isSmartTokenFrom = smartTokenRegistry.isSmartToken(fromToken); bool isSmartTokenTo = smartTokenRegistry.isSmartToken(toToken); @@ -380,7 +379,7 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { IERC20 toToken, uint256 amount, uint256[] memory distribution, - uint256 disableFlags + uint256 flags ) private { SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); @@ -403,7 +402,7 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)), 0, dist, - disableFlags + flags ); } } @@ -413,7 +412,7 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { IERC20 smartToken, uint256 amount, uint256[] memory distribution, - uint256 disableFlags + uint256 flags ) private { uint256[] memory dist = new uint256[](distribution.length); @@ -440,19 +439,19 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { _canonicalSUSD(details.tokens[i].token), exchangeAmount, dist, - disableFlags + flags ); uint256 tokenBalanceAfter = _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)); - curFundAmount = smartTokenFormula.calculatePurchaseReturn( + curFundAmount = smartTokenFormula._calculatePurchaseReturn( smartToken.totalSupply(), _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), uint32(details.totalRatio), tokenBalanceAfter.sub(tokenBalanceBefore) ); } else { - curFundAmount = smartTokenFormula.calculatePurchaseReturn( + curFundAmount = smartTokenFormula._calculatePurchaseReturn( smartToken.totalSupply(), _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), uint32(details.totalRatio), @@ -475,7 +474,7 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { uint256 leftover = _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)); - uint256 ret = _swapOnBancorSafe( + uint256 ret = this._swapOnBancorSafe( reserveToken, smartToken, leftover diff --git a/contracts/OneSplitUniswapPoolToken.sol b/contracts/OneSplitUniswapPoolToken.sol index 30852f3..56b6e3c 100644 --- a/contracts/OneSplitUniswapPoolToken.sol +++ b/contracts/OneSplitUniswapPoolToken.sol @@ -57,7 +57,7 @@ contract OneSplitUniswapPoolTokenView is OneSplitViewWrapBase, OneSplitUniswapPo IERC20 toToken, uint256 amount, uint256 parts, - uint256 disableFlags + uint256 flags ) public view @@ -71,7 +71,7 @@ contract OneSplitUniswapPoolTokenView is OneSplitViewWrapBase, OneSplitUniswapPo } - if (!disableFlags.check(FLAG_DISABLE_UNISWAP_POOL_TOKEN)) { + if (!flags.check(FLAG_DISABLE_UNISWAP_POOL_TOKEN)) { bool isPoolTokenFrom = isLiquidityPool(fromToken); bool isPoolTokenTo = isLiquidityPool(toToken); @@ -131,7 +131,7 @@ contract OneSplitUniswapPoolTokenView is OneSplitViewWrapBase, OneSplitUniswapPo toToken, amount, parts, - disableFlags + flags ); } @@ -140,7 +140,7 @@ contract OneSplitUniswapPoolTokenView is OneSplitViewWrapBase, OneSplitUniswapPo IERC20 toToken, uint256 amount, uint256 parts, - uint256 disableFlags + uint256 flags ) private view @@ -165,7 +165,7 @@ contract OneSplitUniswapPoolTokenView is OneSplitViewWrapBase, OneSplitUniswapPo toToken, ethAmount, parts, - disableFlags + flags ); returnAmount = returnAmount.add(ret); @@ -185,7 +185,7 @@ contract OneSplitUniswapPoolTokenView is OneSplitViewWrapBase, OneSplitUniswapPo toToken, exchangeTokenAmount, parts, - disableFlags + flags ); returnAmount = returnAmount.add(ret); @@ -204,7 +204,7 @@ contract OneSplitUniswapPoolTokenView is OneSplitViewWrapBase, OneSplitUniswapPo IERC20 poolToken, uint256 amount, uint256 parts, - uint256 disableFlags + uint256 flags ) private view @@ -226,7 +226,7 @@ contract OneSplitUniswapPoolTokenView is OneSplitViewWrapBase, OneSplitUniswapPo ETH_ADDRESS, partAmountForEth, parts, - disableFlags + flags ); for (uint j = 0; j < distribution.length; j++) { @@ -246,7 +246,7 @@ contract OneSplitUniswapPoolTokenView is OneSplitViewWrapBase, OneSplitUniswapPo uniswapToken, partAmountForToken, parts, - disableFlags + flags ); for (uint j = 0; j < distribution.length; j++) { @@ -278,13 +278,13 @@ contract OneSplitUniswapPoolToken is OneSplitBaseWrap, OneSplitUniswapPoolTokenB IERC20 toToken, uint256 amount, uint256[] memory distribution, - uint256 disableFlags + uint256 flags ) internal { if (fromToken == toToken) { return; } - if (!disableFlags.check(FLAG_DISABLE_UNISWAP_POOL_TOKEN)) { + if (!flags.check(FLAG_DISABLE_UNISWAP_POOL_TOKEN)) { bool isPoolTokenFrom = isLiquidityPool(fromToken); bool isPoolTokenTo = isLiquidityPool(toToken); @@ -345,7 +345,7 @@ contract OneSplitUniswapPoolToken is OneSplitBaseWrap, OneSplitUniswapPoolTokenB toToken, amount, distribution, - disableFlags + flags ); } @@ -354,7 +354,7 @@ contract OneSplitUniswapPoolToken is OneSplitBaseWrap, OneSplitUniswapPoolTokenB IERC20 toToken, uint256 amount, uint256[] memory distribution, - uint256 disableFlags + uint256 flags ) private { uint256[] memory dist = new uint256[](distribution.length); @@ -379,7 +379,7 @@ contract OneSplitUniswapPoolToken is OneSplitBaseWrap, OneSplitUniswapPoolTokenB toToken, ethAmount, dist, - disableFlags + flags ); } @@ -395,7 +395,7 @@ contract OneSplitUniswapPoolToken is OneSplitBaseWrap, OneSplitUniswapPoolTokenB toToken, exchangeTokenAmount, dist, - disableFlags + flags ); } } @@ -405,7 +405,7 @@ contract OneSplitUniswapPoolToken is OneSplitBaseWrap, OneSplitUniswapPoolTokenB IERC20 poolToken, uint256 amount, uint256[] memory distribution, - uint256 disableFlags + uint256 flags ) private { uint256[] memory dist = new uint256[](distribution.length); @@ -420,7 +420,7 @@ contract OneSplitUniswapPoolToken is OneSplitBaseWrap, OneSplitUniswapPoolTokenB ETH_ADDRESS, partAmountForEth, dist, - disableFlags + flags ); } @@ -437,7 +437,7 @@ contract OneSplitUniswapPoolToken is OneSplitBaseWrap, OneSplitUniswapPoolTokenB uniswapToken, partAmountForToken, dist, - disableFlags + flags ); _infiniteApproveIfNeeded(uniswapToken, address(poolToken)); diff --git a/contracts/interface/ISmartTokenFormula.sol b/contracts/interface/ISmartTokenFormula.sol index 1dc791c..c4f2119 100644 --- a/contracts/interface/ISmartTokenFormula.sol +++ b/contracts/interface/ISmartTokenFormula.sol @@ -4,14 +4,14 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface ISmartTokenFormula { - function calculateLiquidateReturn( + function _calculateLiquidateReturn( uint256 supply, uint256 reserveBalance, uint32 totalRatio, uint256 amount ) external view returns (uint256); - function calculatePurchaseReturn( + function _calculatePurchaseReturn( uint256 supply, uint256 reserveBalance, uint32 totalRatio, From 3f0746ff39899cf93d7d8bdabf84b0ea43530f72 Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 4 May 2020 02:23:57 +0300 Subject: [PATCH 51/60] done with all curve tokens --- OneSplit.full.abi | 2 +- OneSplit.full.bin | 2 +- OneSplit.full.sol | 2257 ++++++++++++++++++---- contracts/IOneSplit.sol | 25 +- contracts/OneSplit.sol | 6 +- contracts/OneSplitCurvePoolToken.sol | 452 +++++ contracts/OneSplitCurveSusdPoolToken.sol | 319 --- contracts/interface/ICurve.sol | 6 - 8 files changed, 2382 insertions(+), 687 deletions(-) create mode 100644 contracts/OneSplitCurvePoolToken.sol delete mode 100644 contracts/OneSplitCurveSusdPoolToken.sol diff --git a/OneSplit.full.abi b/OneSplit.full.abi index ac96424..923bcab 100644 --- a/OneSplit.full.abi +++ b/OneSplit.full.abi @@ -1 +1 @@ -[{"inputs":[{"internalType":"contract IOneSplitView","name":"_oneSplitView","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[],"name":"DEXES_COUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ETH_ADDRESS","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BANCOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BDAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_BINANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_SYNTHETIX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_USDT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_Y","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_FULCRUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IDLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IEARN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_KYBER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_OASIS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_SMART_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_WETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_BANCOR_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_OASIS_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_UNISWAP_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_DAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_USDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"aave","outputs":[{"internalType":"contract IAaveLendingPool","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorContractRegistry","outputs":[{"internalType":"contract IBancorContractRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorConverterRegistry","outputs":[{"internalType":"contract IBancorConverterRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorEtherToken","outputs":[{"internalType":"contract IBancorEtherToken","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bnt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"busd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cETH","outputs":[{"internalType":"contract ICompoundEther","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"chai","outputs":[{"internalType":"contract IChai","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"compound","outputs":[{"internalType":"contract ICompound","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveBinance","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveCompound","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveSynthetix","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveUsdt","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveY","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"}],"name":"getExpectedReturn","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"kyberNetworkProxy","outputs":[{"internalType":"contract IKyberNetworkProxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oasisExchange","outputs":[{"internalType":"contract IOasisExchange","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oneSplitView","outputs":[{"internalType":"contract IOneSplitView","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"susd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"swap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"tusd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"uniswapFactory","outputs":[{"internalType":"contract IUniswapFactory","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdc","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"wethToken","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file +[{"inputs":[{"internalType":"contract IOneSplitView","name":"_oneSplitView","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[],"name":"DEXES_COUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ETH_ADDRESS","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BALANCER_POOL_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BANCOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BDAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_BINANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_SYNTHETIX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_USDT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_Y","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_ZAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_FULCRUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IDLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IEARN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_KYBER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_OASIS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_SMART_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_POOL_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_WETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_BANCOR_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_OASIS_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_UNISWAP_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_DAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_USDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"aave","outputs":[{"internalType":"contract IAaveLendingPool","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorContractRegistry","outputs":[{"internalType":"contract IBancorContractRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorConverterRegistry","outputs":[{"internalType":"contract IBancorConverterRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorEtherToken","outputs":[{"internalType":"contract IBancorEtherToken","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bnt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"busd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cETH","outputs":[{"internalType":"contract ICompoundEther","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"chai","outputs":[{"internalType":"contract IChai","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"compound","outputs":[{"internalType":"contract ICompound","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveBinance","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveCompound","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveSynthetix","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveUsdt","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveY","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"}],"name":"getExpectedReturn","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"kyberNetworkProxy","outputs":[{"internalType":"contract IKyberNetworkProxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oasisExchange","outputs":[{"internalType":"contract IOasisExchange","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oneSplitView","outputs":[{"internalType":"contract IOneSplitView","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"susd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"swap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"tusd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"uniswapFactory","outputs":[{"internalType":"contract IUniswapFactory","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdc","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"wethToken","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/OneSplit.full.bin b/OneSplit.full.bin index 5c5cece..834cefb 100644 --- a/OneSplit.full.bin +++ b/OneSplit.full.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b50604051613e86380380613e868339818101604052602081101561003357600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055613e21806100656000396000f3fe6080604052600436106103505760003560e01c806375a8b012116101c6578063c9257775116100f7578063d77366a411610095578063f4b9fa751161006f578063f4b9fa7514610906578063f56e281f1461091b578063f69e204614610930578063fbe4ed951461094557610350565b8063d77366a414610812578063dc1536b214610827578063e2a7515e1461083c57610350565b8063cc26e9fc116100d1578063cc26e9fc146107be578063cede5f6a146107d3578063d393c3e9146107e8578063d70a2d1f146107fd57610350565b8063c92577751461077f578063c989b66714610794578063c9b42c67146107a957610350565b8063a1b4d01111610164578063b3bc78441161013e578063b3bc78441461072b578063b69d045614610740578063c762a46c14610755578063c77b9de61461076a57610350565b8063a1b4d011146106ec578063a734f06e14610701578063b0a7ef291461071657610350565b80637e09b9c2116101a05780637e09b9c214610698578063819faf7b146106ad578063851954fa146106c25780638bdb2afa146106d757610350565b806375a8b0121461065957806375b5be2d1461066e5780637a88bdbd1461068357610350565b80633ca5b234116102a057806351f1985c1161023e5780635c0cb479116102185780635c0cb4791461060557806364ec4e5c1461061a57806368e2a0141461062f5780636cbc4a6e1461064457610350565b806351f1985c146105c65780635aa8fb48146105db5780635ae51b82146105f057610350565b8063423d03f91161027a578063423d03f91461057257806344211d62146105875780634a7101d51461059c5780634b57b0be146105b157610350565b80633ca5b234146105335780633e413bee1461054857806340ab7b8c1461055d57610350565b806321a360f51161030d5780632e707bd2116102e75780632e707bd2146104df5780632f48ab7d146104f457806334b4dabb14610509578063372a26cb1461051e57610350565b806321a360f5146104a057806322320c98146104b55780632d3b5207146104ca57610350565b806305d8aa0a1461035f578063085e2c5b1461038657806312dea160146104305780631388b4201461046157806313989140146104765780632113240d1461048b575b3332141561035d57600080fd5b005b34801561036b57600080fd5b5061037461095a565b60408051918252519081900360200190f35b34801561039257600080fd5b506103d5600480360360a08110156103a957600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060800135610961565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b8381101561041b578181015183820152602001610403565b50505050905001935050505060405180910390f35b34801561043c57600080fd5b50610445610aad565b604080516001600160a01b039092168252519081900360200190f35b34801561046d57600080fd5b50610445610ac5565b34801561048257600080fd5b50610374610add565b34801561049757600080fd5b50610374610ae3565b3480156104ac57600080fd5b50610374610ae9565b3480156104c157600080fd5b50610445610af2565b3480156104d657600080fd5b50610374610b0a565b3480156104eb57600080fd5b50610374610b13565b34801561050057600080fd5b50610445610b18565b34801561051557600080fd5b50610374610b30565b34801561052a57600080fd5b50610445610b35565b34801561053f57600080fd5b50610445610b4d565b34801561055457600080fd5b50610445610b65565b34801561056957600080fd5b50610445610b77565b34801561057e57600080fd5b50610445610b8f565b34801561059357600080fd5b50610374610ba7565b3480156105a857600080fd5b50610374610bac565b3480156105bd57600080fd5b50610445610bb1565b3480156105d257600080fd5b50610445610bc9565b3480156105e757600080fd5b50610374610be1565b3480156105fc57600080fd5b50610374610be7565b34801561061157600080fd5b50610374610bed565b34801561062657600080fd5b50610374610bf2565b34801561063b57600080fd5b50610374610bf9565b34801561065057600080fd5b50610374610c00565b34801561066557600080fd5b50610374610c07565b34801561067a57600080fd5b50610445610c0d565b34801561068f57600080fd5b50610374610c20565b3480156106a457600080fd5b50610374610c25565b3480156106b957600080fd5b50610445610c2c565b3480156106ce57600080fd5b50610445610c44565b3480156106e357600080fd5b50610445610c5c565b3480156106f857600080fd5b50610445610c74565b34801561070d57600080fd5b50610445610c8c565b34801561072257600080fd5b50610374610ca4565b34801561073757600080fd5b50610374610caa565b34801561074c57600080fd5b50610445610cb3565b34801561076157600080fd5b50610374610ccb565b34801561077657600080fd5b50610374610cd0565b34801561078b57600080fd5b50610445610cd6565b3480156107a057600080fd5b50610374610cee565b3480156107b557600080fd5b50610374610cf5565b3480156107ca57600080fd5b50610374610cfc565b3480156107df57600080fd5b50610445610d01565b3480156107f457600080fd5b50610374610d19565b34801561080957600080fd5b50610445610d20565b34801561081e57600080fd5b50610445610d38565b34801561083357600080fd5b50610374610d50565b61035d600480360360c081101561085257600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a08101608082013564010000000081111561089257600080fd5b8201836020820111156108a457600080fd5b803590602001918460208302840111640100000000831117156108c657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610d56915050565b34801561091257600080fd5b50610445610f78565b34801561092757600080fd5b50610374610f8a565b34801561093c57600080fd5b50610445610f8f565b34801561095157600080fd5b50610445610fa7565b6220000081565b600080546040805163085e2c5b60e01b81526001600160a01b03898116600483015288811660248301526044820188905260648201879052608482018690529151606093929092169163085e2c5b9160a4808201928792909190829003018186803b1580156109cf57600080fd5b505afa1580156109e3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610a0c57600080fd5b815160208301805160405192949293830192919084640100000000821115610a3357600080fd5b908301906020820185811115610a4857600080fd5b8251866020820283011164010000000082111715610a6557600080fd5b82525081516020918201928201910280838360005b83811015610a92578181015183820152602001610a7a565b50505050905001604052505050915091509550959350505050565b7352ae12abe5d8bd778bd5397f99ca900624cfadd481565b73794e6e91555438afc3ccf1c5076a74f42133d08d81565b61200081565b61800081565b64020000000081565b73a5407eae9ba41422680e2e00537571bcc53efbfd81565b64010000000081565b608081565b73dac17f958d2ee523a2206206994597c13d831ec781565b604081565b7379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2781565b734fabb145d64652a948d72533023f6e7a623c7c5381565b600080516020613d2b83398151915281565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c81565b7345f783cce6b7ff23b2ab2d70e416cdb7d6055f5181565b601081565b602081565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b73a2b47e3d5c44877cca798226b7b8118f9bfb7a5681565b61400081565b61080081565b600881565b6202000081565b6210000081565b6208000081565b61040081565b6e085d4780b73119b644ae5ecd22b37681565b600281565b6240000081565b73398ec7346dcd622edc5ae82352f02be94c62d11981565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce31581565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9581565b734ddc2d193948926d02f9b1fe9e1daa0718270ed581565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b7306af07097c9eeb7fd685c692751d5c66db49c21581565b600181565b61020081565b7357ab1ec28d129707052df4df418d58a2d46d5f5181565b6280000081565b6204000081565b600c81565b7352ea46506b9cc5ef470c5bf89f17dc28bb35d85c81565b6201000081565b73f6e2d7f616b67e46d708e4410746e9aab3a4c51881565b73818e6fecd516ecc3849daf6845e3ec868087b75581565b61010081565b846001600160a01b0316866001600160a01b03161415610d7557610f70565b610d7d613c8f565b604051806101800160405280610fb6815260200161123781526020016113c281526020016116e781526020016119c08152602001611b4b8152602001611d1c8152602001611f418152602001612170815260200161239f815260200161253d81526020016126e98152509050600c83511115610e2a5760405162461bcd60e51b8152600401808060200182810382526042815260200180613dab6042913960600191505060405180910390fd5b600080805b8551811015610e88576000868281518110610e4657fe5b60200260200101511115610e8057610e7a868281518110610e6357fe5b60200260200101518461285590919063ffffffff16565b92508091505b600101610e2f565b5060008211610ec85760405162461bcd60e51b815260040180806020018281038252602f815260200180613cdb602f913960400191505060405180910390fd5b8660005b8651811015610f6a57868181518110610ee157fe5b602002602001015160001415610ef657610f62565b6000610f2e85610f228a8581518110610f0b57fe5b60200260200101518d6128b890919063ffffffff16565b9063ffffffff61291116565b905083821415610f3b5750815b8083039250610f5f8c8c838986600c8110610f5257fe5b602002015163ffffffff16565b50505b600101610ecc565b50505050505b505050505050565b600080516020613cbb83398151915281565b600481565b733d9819210a31b4961b30ef54be2aed79b9c9cd3b81565b6000546001600160a01b031681565b600081610fcb6001600160a01b038616612953565b6110fb57604080516303795fb160e11b81526001600160a01b0387166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b15801561102d57600080fd5b505afa158015611041573d6000803e3d6000fd5b505050506040513d602081101561105757600080fd5b505190506001600160a01b038116156110f957611074868261298f565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b1580156110ca57600080fd5b505af11580156110de573d6000803e3d6000fd5b505050506040513d60208110156110f457600080fd5b505191505b505b61110d846001600160a01b0316612953565b61122d57604080516303795fb160e11b81526001600160a01b0386166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b15801561116f57600080fd5b505afa158015611183573d6000803e3d6000fd5b505050506040513d602081101561119957600080fd5b505190506001600160a01b0381161561122b57806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b1580156111fb57600080fd5b505af115801561120f573d6000803e3d6000fd5b50505050506040513d602081101561122657600080fd5b505191505b505b90505b9392505050565b60006112578473818e6fecd516ecc3849daf6845e3ec868087b75561298f565b73818e6fecd516ecc3849daf6845e3ec868087b7556329589f616112836001600160a01b038716612953565b61128e576000611290565b835b6112a2876001600160a01b0316612953565b6112ac57866112c2565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b856112d5886001600160a01b0316612953565b6112df57876112f5565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b15801561138d57600080fd5b505af11580156113a1573d6000803e3d6000fd5b50505050506040513d60208110156113b857600080fd5b5051949350505050565b60006113d6846001600160a01b0316612953565b156114445773c0829421c1d260bd3cb3e0f06cfe2d52db2ce3156001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561142a57600080fd5b505af115801561143e573d6000803e3d6000fd5b50505050505b60007352ae12abe5d8bd778bd5397f99ca900624cfadd46001600160a01b031663bb34534c6040518163ffffffff1660e01b815260040180806c42616e636f724e6574776f726b60981b815250602001905060206040518083038186803b1580156114ae57600080fd5b505afa1580156114c2573d6000803e3d6000fd5b505050506040513d60208110156114d857600080fd5b5051905060606114e88686612a48565b90506115256114ff876001600160a01b0316612953565b611509578661151f565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b8361298f565b6000826001600160a01b031663c7ba24bc838760016040518463ffffffff1660e01b81526004018080602001848152602001838152602001828103825285818151815260200191508051906020019060200280838360005b8381101561159557818101518382015260200161157d565b50505050905001945050505050602060405180830381600087803b1580156115bc57600080fd5b505af11580156115d0573d6000803e3d6000fd5b505050506040513d60208110156115e657600080fd5b505190506115fc6001600160a01b038716612953565b156116dd57604080516370a0823160e01b8152306004820152905173c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591632e1a7d4d9183916370a08231916024808301926020929190829003018186803b15801561165a57600080fd5b505afa15801561166e573d6000803e3d6000fd5b505050506040513d602081101561168457600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b1580156116c457600080fd5b505af11580156116d8573d6000803e3d6000fd5b505050505b9695505050505050565b60006116fb846001600160a01b0316612953565b156117695773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561174f57600080fd5b505af1158015611763573d6000803e3d6000fd5b50505050505b6117b861177e856001600160a01b0316612953565b611788578461179e565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b73794e6e91555438afc3ccf1c5076a74f42133d08d61298f565b600073794e6e91555438afc3ccf1c5076a74f42133d08d630621b4f66117e66001600160a01b038816612953565b6117f05786611806565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b85611819886001600160a01b0316612953565b6118235787611839565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b15801561189757600080fd5b505af11580156118ab573d6000803e3d6000fd5b505050506040513d60208110156118c157600080fd5b505190506118d76001600160a01b038516612953565b1561122d57604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b15801561193557600080fd5b505afa158015611949573d6000803e3d6000fd5b505050506040513d602081101561195f57600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561199f57600080fd5b505af11580156119b3573d6000803e3d6000fd5b5050505090509392505050565b6000806001600160a01b038516600080516020613d2b833981519152146119e85760006119eb565b60025b6001600160a01b038616600080516020613cbb83398151915214611a10576000611a13565b60015b0160ff1690506000600080516020613d2b8339815191526001600160a01b03861614611a40576000611a43565b60025b6001600160a01b038616600080516020613cbb83398151915214611a68576000611a6b565b60015b0160ff16905081600f0b60001480611a86575080600f0b6000145b15611a9657600092505050611230565b611ab48673a2b47e3d5c44877cca798226b7b8118f9bfb7a5661298f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a2b47e3d5c44877cca798226b7b8118f9bfb7a569263a6417ed6926084808201939182900301818387803b158015611b2a57600080fd5b505af1158015611b3e573d6000803e3d6000fd5b5050505050509392505050565b6000806001600160a01b03851673dac17f958d2ee523a2206206994597c13d831ec714611b79576000611b7c565b60035b6001600160a01b038616600080516020613d2b83398151915214611ba1576000611ba4565b60025b6001600160a01b038716600080516020613cbb83398151915214611bc9576000611bcc565b60015b010160ff169050600073dac17f958d2ee523a2206206994597c13d831ec76001600160a01b0316856001600160a01b031614611c09576000611c0c565b60035b6001600160a01b038616600080516020613d2b83398151915214611c31576000611c34565b60025b6001600160a01b038716600080516020613cbb83398151915214611c59576000611c5c565b60015b010160ff16905081600f0b60001480611c78575080600f0b6000145b15611c8857600092505050611230565b611ca6867352ea46506b9cc5ef470c5bf89f17dc28bb35d85c61298f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517352ea46506b9cc5ef470c5bf89f17dc28bb35d85c9263a6417ed6926084808201939182900301818387803b158015611b2a57600080fd5b6000806001600160a01b0385166e085d4780b73119b644ae5ecd22b37614611d45576000611d48565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611d73576000611d76565b60035b6001600160a01b038716600080516020613d2b83398151915214611d9b576000611d9e565b60025b6001600160a01b038816600080516020613cbb83398151915214611dc3576000611dc6565b60015b01010160ff16905060006e085d4780b73119b644ae5ecd22b3766001600160a01b0316856001600160a01b031614611dff576000611e02565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611e2d576000611e30565b60035b6001600160a01b038716600080516020613d2b83398151915214611e55576000611e58565b60025b6001600160a01b038816600080516020613cbb83398151915214611e7d576000611e80565b60015b01010160ff16905081600f0b60001480611e9d575080600f0b6000145b15611ead57600092505050611230565b611ecb867345f783cce6b7ff23b2ab2d70e416cdb7d6055f5161298f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517345f783cce6b7ff23b2ab2d70e416cdb7d6055f519263a6417ed6926084808201939182900301818387803b158015611b2a57600080fd5b6000806001600160a01b038516734fabb145d64652a948d72533023f6e7a623c7c5314611f6f576000611f72565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611f9d576000611fa0565b60035b6001600160a01b038716600080516020613d2b83398151915214611fc5576000611fc8565b60025b6001600160a01b038816600080516020613cbb83398151915214611fed576000611ff0565b60015b01010160ff1690506000734fabb145d64652a948d72533023f6e7a623c7c536001600160a01b0316856001600160a01b03161461202e576000612031565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec71461205c57600061205f565b60035b6001600160a01b038716600080516020613d2b83398151915214612084576000612087565b60025b6001600160a01b038816600080516020613cbb833981519152146120ac5760006120af565b60015b01010160ff16905081600f0b600014806120cc575080600f0b6000145b156120dc57600092505050611230565b6120fa867379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2761298f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517379a8c46dea5ada233abaffd40f3a0a2b1e5a4f279263a6417ed6926084808201939182900301818387803b158015611b2a57600080fd5b6000806001600160a01b0385167357ab1ec28d129707052df4df418d58a2d46d5f511461219e5760006121a1565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec7146121cc5760006121cf565b60035b6001600160a01b038716600080516020613d2b833981519152146121f45760006121f7565b60025b6001600160a01b038816600080516020613cbb8339815191521461221c57600061221f565b60015b01010160ff16905060007357ab1ec28d129707052df4df418d58a2d46d5f516001600160a01b0316856001600160a01b03161461225d576000612260565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec71461228b57600061228e565b60035b6001600160a01b038716600080516020613d2b833981519152146122b35760006122b6565b60025b6001600160a01b038816600080516020613cbb833981519152146122db5760006122de565b60015b01010160ff16905081600f0b600014806122fb575080600f0b6000145b1561230b57600092505050611230565b6123298673a5407eae9ba41422680e2e00537571bcc53efbfd61298f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a5407eae9ba41422680e2e00537571bcc53efbfd9263a6417ed6926084808201939182900301818387803b158015611b2a57600080fd5b60006123b3846001600160a01b0316612953565b61246c5760006123c285613152565b90506123ce858261298f565b806001600160a01b031663a0712d68846040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561241457600080fd5b505af1158015612428573d6000803e3d6000fd5b505050506040513d602081101561243e57600080fd5b506124649050818561245f6001600160a01b0383163063ffffffff61334216565b610fb6565b915050611230565b61247e836001600160a01b0316612953565b61253357600061248d84613152565b9050600061249c868386610fb6565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156124e457600080fd5b505af11580156124f8573d6000803e3d6000fd5b505050506040513d602081101561250e57600080fd5b5061252a90506001600160a01b0386163063ffffffff61334216565b92505050611230565b5060009392505050565b60006001600160a01b038416600080516020613cbb83398151915214156126205761257c847306af07097c9eeb7fd685c692751d5c66db49c21561298f565b60408051633b4da69f60e01b81523060048201526024810184905290517306af07097c9eeb7fd685c692751d5c66db49c21591633b4da69f91604480830192600092919082900301818387803b1580156125d557600080fd5b505af11580156125e9573d6000803e3d6000fd5b5061261992507306af07097c9eeb7fd685c692751d5c66db49c215915085905061245f823063ffffffff61334216565b9050611230565b6001600160a01b038316600080516020613cbb8339815191521415612533576000612660857306af07097c9eeb7fd685c692751d5c66db49c21585610fb6565b6040805163ef693bed60e01b81523060048201526024810183905290519192507306af07097c9eeb7fd685c692751d5c66db49c2159163ef693bed9160448082019260009290919082900301818387803b1580156126bd57600080fd5b505af11580156126d1573d6000803e3d6000fd5b50612464925050506001600160a01b03851630613342565b60006126fd846001600160a01b0316612953565b6127bb57600061270c856133ec565b9050612718858261298f565b60408051636968703360e11b81526001600160a01b03871660048201526024810185905261044d6044820152905173398ec7346dcd622edc5ae82352f02be94c62d1199163d2d0e06691606480830192600092919082900301818387803b15801561278257600080fd5b505af1158015612796573d6000803e3d6000fd5b50505050612464818561245f30856001600160a01b031661334290919063ffffffff16565b6127cd836001600160a01b0316612953565b6125335760006127dc846133ec565b905060006127eb868386610fb6565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561283357600080fd5b505af1158015612847573d6000803e3d6000fd5b505050508092505050611230565b6000828201838110156128af576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b6000826128c7575060006128b2565b828202828482816128d457fe5b04146128af5760405162461bcd60e51b8152600401808060200182810382526021815260200180613d0a6021913960400191505060405180910390fd5b60006128af83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061380f565b60006001600160a01b038216158061298757506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b90505b919050565b6129a1826001600160a01b0316612953565b612a445760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b1580156129f657600080fd5b505afa158015612a0a573d6000803e3d6000fd5b505050506040513d6020811015612a2057600080fd5b5051901c612a4457612a446001600160a01b0383168260001963ffffffff6138b116565b5050565b6060816001600160a01b0316836001600160a01b03161415612a7957506040805160008152602081019091526128b2565b612a8b836001600160a01b0316612953565b15612aa85773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31592505b612aba826001600160a01b0316612953565b15612ad75773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591505b6001600160a01b038316731f573d6fb3f13d689ff844b4ce37794d79a7ff1c1480612b1e57506001600160a01b038216731f573d6fb3f13d689ff844b4ce37794d79a7ff1c145b15612b4957604080516003808252608082019092529060208201606080388339019050509050612b6b565b60408051600580825260c08201909252906020820160a0803883390190505090505b6000806001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14612d34576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b612bc86001600160a01b038b16612953565b612bd25789612be8565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b60208310612c665780518252601f199092019160209182019101612c47565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114612cc7576040519150601f19603f3d011682016040523d82523d6000602084013e612ccc565b606091505b509150915081612cf45760408051600080825260208201909252905b509450505050506128b2565b808060200190516020811015612d0957600080fd5b505193506001600160a01b038416612d31576040805160008082526020820190925290612ce8565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14612ef2576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b612d8e6001600160a01b038a16612953565b612d985788612dae565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b60208310612e2c5780518252601f199092019160209182019101612e0d565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114612e8d576040519150601f19603f3d011682016040523d82523d6000602084013e612e92565b606091505b509150915081612eb2576040805160008082526020820190925290612ce8565b808060200190516020811015612ec757600080fd5b505192506001600160a01b038316612eef576040805160008082526020820190925290612ce8565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c1415612fb5578483600081518110612f2557fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508183600181518110612f5357fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c83600281518110612f9557fe5b6001600160a01b0390921660209283029190910190910152506128b29050565b6001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c141561305857731f573d6fb3f13d689ff844b4ce37794d79a7ff1c83600081518110612ffc57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050808360018151811061302a57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508383600281518110612f9557fe5b848360008151811061306657fe5b60200260200101906001600160a01b031690816001600160a01b031681525050818360018151811061309457fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c836002815181106130d657fe5b60200260200101906001600160a01b031690816001600160a01b031681525050808360038151811061310457fe5b60200260200101906001600160a01b031690816001600160a01b031681525050838360048151811061313257fe5b6001600160a01b0390921660209283029190910190910152505092915050565b6000613166826001600160a01b0316612953565b156131865750734ddc2d193948926d02f9b1fe9e1daa0718270ed561298a565b6001600160a01b038216600080516020613cbb83398151915214156131c05750735d3a536e4d6dbd6114cc1ead35777bab948e364361298a565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef14156132005750736c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e61298a565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e8621415613240575073158079ee67fce2f58472a96584a73c7ab9ac95c161298a565b6001600160a01b038216600080516020613d2b833981519152141561327a57507339aa39c021dfbae8fac545936693ac917d5e756361298a565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c59914156132ba575073c11b1268c1a384e55c48c2391d8d480264a3a7f461298a565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f49814156132fa575073b3319f5d18bc0d84dd1b4825dcde5d5f7266d40761298a565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec7141561333a575073f650c3d88d12db855b8bf7d11be6c55a4e07dcc961298a565b506000919050565b600061334d83612953565b1561336357506001600160a01b038116316128b2565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156133b957600080fd5b505afa1580156133cd573d6000803e3d6000fd5b505050506040513d60208110156133e357600080fd5b505190506128b2565b6000613400826001600160a01b0316612953565b156134205750733a3a65aab0dd2a17e3f1947ba16138cd37d08c0461298a565b6001600160a01b038216600080516020613cbb833981519152141561345a575073fc1e690f61efd961294b3e1ce3313fbd8aa4f85d61298a565b6001600160a01b038216600080516020613d2b83398151915214156134945750739ba00d6856a4edf4665bca2c2309936572473b7e61298a565b6001600160a01b0382167357ab1ec28d129707052df4df418d58a2d46d5f5114156134d4575073625ae63000f46200499120b906716420bd05924061298a565b6001600160a01b038216734fabb145d64652a948d72533023f6e7a623c7c5314156135145750736ee0f7bb50a54ab5253da0667b0dc2ee526c30a861298a565b6001600160a01b0382166e085d4780b73119b644ae5ecd22b376141561354f5750734da9b813057d04baef4e5800e36083717b4a034161298a565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec7141561358f57507371fc860f7d3a592a4a98740e39db31d25db65ae861298a565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef14156135cf575073e1ba0fb44ccb0d11b80f92f4f8ed94ca3ff51d0061298a565b6001600160a01b03821673dd974d5c2e2928dea5f71b9825b8b646686bd200141561360f5750739d91be44c06d373a8a226e1f3b146956083803eb61298a565b6001600160a01b0382167380fb784b7ed66730e8b1dbd9820afd29931aab03141561364f5750737d2d3688df45ce7c552e19c27e007673da9204b861298a565b6001600160a01b03821673514910771af9ca656af840dff83e8264ecf986ca141561368f575073a64bd6c70cb9051f6a9ba1f163fdc07e0dfb5f8461298a565b6001600160a01b038216730f5d2fb29fb7d3cfee444a200298f468908cc94214156136cf5750736fce4a401b6b80ace52baaefe4421bd188e76f6f61298a565b6001600160a01b038216739f8f72aa9304c8b593d555f12ef6589cc3a579a2141561370f5750737deb5e830be29f91e298ba5ff1356bb7f814699861298a565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e862141561374f57507371010a9d003445ac60c4e6a7017c1e89a477b43861298a565b6001600160a01b03821673c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f141561378f575073328c4c80bc7aca0834db37e6600a6c49e12da4de61298a565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c59914156137cf575073fc4b8ed459e00e5400be803a9bb3954234fd50e361298a565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f498141561333a5750736fb0855c404e09c47c3fbca25f08d4e41f9f062f61298a565b6000818361389b5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015613860578181015183820152602001613848565b50505050905090810190601f16801561388d5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816138a757fe5b0495945050505050565b6138ba83612953565b61398257600081118015613948575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561391a57600080fd5b505afa15801561392e573d6000803e3d6000fd5b505050506040513d602081101561394457600080fd5b5051115b15613968576139686001600160a01b03841683600063ffffffff61398716565b6139826001600160a01b038416838363ffffffff61398716565b505050565b801580613a0d575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b1580156139df57600080fd5b505afa1580156139f3573d6000803e3d6000fd5b505050506040513d6020811015613a0957600080fd5b5051155b613a485760405162461bcd60e51b8152600401808060200182810382526036815260200180613d756036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613982908490613aa7826001600160a01b0316613c53565b613af8576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310613b365780518252601f199092019160209182019101613b17565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613b98576040519150601f19603f3d011682016040523d82523d6000602084013e613b9d565b606091505b509150915081613bf4576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115613c4d57808060200190516020811015613c1057600080fd5b5051613c4d5760405162461bcd60e51b815260040180806020018281038252602a815260200180613d4b602a913960400191505060405180910390fd5b50505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590613c8757508115155b949350505050565b604051806101800160405280600c905b613cb8815260200190600190039081613c9f5790505090565bfefe0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f73536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e63654f6e6553706c69743a20446973747269627574696f6e2061727261792073686f756c64206e6f74206578636565642072657365727665732061727261792073697a65a265627a7a72315820b7b106c8ffa502d2e0dd6598694f6eb143e8f1d38bb5268aaee7f0681a39891b64736f6c63430005100032 +608060405234801561001057600080fd5b50604051613ff4380380613ff48339818101604052602081101561003357600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055613f8f806100656000396000f3fe6080604052600436106103815760003560e01c806375b5be2d116101d1578063c925777511610102578063d70a2d1f116100a0578063f4b9fa751161006f578063f4b9fa7514610976578063f56e281f1461098b578063f69e2046146109a0578063fbe4ed95146109b557610381565b8063d70a2d1f1461086d578063d77366a414610882578063dc1536b214610897578063e2a7515e146108ac57610381565b8063cc26e9fc116100dc578063cc26e9fc14610819578063cede5f6a1461082e578063d1aee5e314610843578063d393c3e91461085857610381565b8063c9257775146107da578063c989b667146107ef578063c9b42c671461080457610381565b8063a1b4d0111161016f578063b3bc784411610149578063b3bc784414610786578063b69d04561461079b578063c762a46c146107b0578063c77b9de6146107c557610381565b8063a1b4d01114610747578063a734f06e1461075c578063b0a7ef291461077157610381565b8063819faf7b116101ab578063819faf7b146106f3578063851954fa146107085780638aea49d21461071d5780638bdb2afa1461073257610381565b806375b5be2d146106b45780637a88bdbd146106c95780637e09b9c2146106de57610381565b80633e413bee116102b657806351f1985c1161025457806364ec4e5c1161022357806364ec4e5c1461066057806368e2a014146106755780636cbc4a6e1461068a57806375a8b0121461069f57610381565b806351f1985c1461060c5780635aa8fb48146106215780635ae51b82146106365780635c0cb4791461064b57610381565b8063423d03f911610290578063423d03f9146105b857806344211d62146105cd5780634a7101d5146105e25780634b57b0be146105f757610381565b80633e413bee1461057957806340ab7b8c1461058e5780634226a9b9146105a357610381565b806322320c98116103235780632f48ab7d116102fd5780632f48ab7d1461052557806334b4dabb1461053a578063372a26cb1461054f5780633ca5b2341461056457610381565b806322320c98146104e65780632d3b5207146104fb5780632e707bd21461051057610381565b80631388b4201161035f5780631388b4201461049257806313989140146104a75780632113240d146104bc57806321a360f5146104d157610381565b806305d8aa0a14610390578063085e2c5b146103b757806312dea16014610461575b3332141561038e57600080fd5b005b34801561039c57600080fd5b506103a56109ca565b60408051918252519081900360200190f35b3480156103c357600080fd5b50610406600480360360a08110156103da57600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608001356109d1565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b8381101561044c578181015183820152602001610434565b50505050905001935050505060405180910390f35b34801561046d57600080fd5b50610476610b1d565b604080516001600160a01b039092168252519081900360200190f35b34801561049e57600080fd5b50610476610b35565b3480156104b357600080fd5b506103a5610b4d565b3480156104c857600080fd5b506103a5610b53565b3480156104dd57600080fd5b506103a5610b59565b3480156104f257600080fd5b50610476610b62565b34801561050757600080fd5b506103a5610b7a565b34801561051c57600080fd5b506103a5610b83565b34801561053157600080fd5b50610476610b88565b34801561054657600080fd5b506103a5610ba0565b34801561055b57600080fd5b50610476610ba5565b34801561057057600080fd5b50610476610bbd565b34801561058557600080fd5b50610476610bd5565b34801561059a57600080fd5b50610476610be7565b3480156105af57600080fd5b506103a5610bff565b3480156105c457600080fd5b50610476610c07565b3480156105d957600080fd5b506103a5610c1f565b3480156105ee57600080fd5b506103a5610c24565b34801561060357600080fd5b50610476610c29565b34801561061857600080fd5b50610476610c41565b34801561062d57600080fd5b506103a5610c59565b34801561064257600080fd5b506103a5610c5f565b34801561065757600080fd5b506103a5610c65565b34801561066c57600080fd5b506103a5610c6a565b34801561068157600080fd5b506103a5610c71565b34801561069657600080fd5b506103a5610c78565b3480156106ab57600080fd5b506103a5610c7f565b3480156106c057600080fd5b50610476610c85565b3480156106d557600080fd5b506103a5610c98565b3480156106ea57600080fd5b506103a5610c9d565b3480156106ff57600080fd5b50610476610ca4565b34801561071457600080fd5b50610476610cbc565b34801561072957600080fd5b506103a5610cd4565b34801561073e57600080fd5b50610476610cdc565b34801561075357600080fd5b50610476610cf4565b34801561076857600080fd5b50610476610d0c565b34801561077d57600080fd5b506103a5610d24565b34801561079257600080fd5b506103a5610d2a565b3480156107a757600080fd5b50610476610d33565b3480156107bc57600080fd5b506103a5610d4b565b3480156107d157600080fd5b506103a5610d50565b3480156107e657600080fd5b50610476610d56565b3480156107fb57600080fd5b506103a5610d6e565b34801561081057600080fd5b506103a5610d75565b34801561082557600080fd5b506103a5610d7c565b34801561083a57600080fd5b50610476610d81565b34801561084f57600080fd5b506103a5610d99565b34801561086457600080fd5b506103a5610da1565b34801561087957600080fd5b50610476610da8565b34801561088e57600080fd5b50610476610dc0565b3480156108a357600080fd5b506103a5610dd8565b61038e600480360360c08110156108c257600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a08101608082013564010000000081111561090257600080fd5b82018360208201111561091457600080fd5b8035906020019184602083028401116401000000008311171561093657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610dde915050565b34801561098257600080fd5b50610476611000565b34801561099757600080fd5b506103a5611012565b3480156109ac57600080fd5b50610476611017565b3480156109c157600080fd5b5061047661102f565b6220000081565b600080546040805163085e2c5b60e01b81526001600160a01b03898116600483015288811660248301526044820188905260648201879052608482018690529151606093929092169163085e2c5b9160a4808201928792909190829003018186803b158015610a3f57600080fd5b505afa158015610a53573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610a7c57600080fd5b815160208301805160405192949293830192919084640100000000821115610aa357600080fd5b908301906020820185811115610ab857600080fd5b8251866020820283011164010000000082111715610ad557600080fd5b82525081516020918201928201910280838360005b83811015610b02578181015183820152602001610aea565b50505050905001604052505050915091509550959350505050565b7352ae12abe5d8bd778bd5397f99ca900624cfadd481565b73794e6e91555438afc3ccf1c5076a74f42133d08d81565b61200081565b61800081565b64020000000081565b73a5407eae9ba41422680e2e00537571bcc53efbfd81565b64010000000081565b608081565b73dac17f958d2ee523a2206206994597c13d831ec781565b604081565b7379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2781565b734fabb145d64652a948d72533023f6e7a623c7c5381565b600080516020613e9983398151915281565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c81565b630100000081565b7345f783cce6b7ff23b2ab2d70e416cdb7d6055f5181565b601081565b602081565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b73a2b47e3d5c44877cca798226b7b8118f9bfb7a5681565b61400081565b61080081565b600881565b6202000081565b6210000081565b6208000081565b61040081565b6e085d4780b73119b644ae5ecd22b37681565b600281565b6240000081565b73398ec7346dcd622edc5ae82352f02be94c62d11981565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce31581565b630400000081565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9581565b734ddc2d193948926d02f9b1fe9e1daa0718270ed581565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b7306af07097c9eeb7fd685c692751d5c66db49c21581565b600181565b61020081565b7357ab1ec28d129707052df4df418d58a2d46d5f5181565b6280000081565b6204000081565b600c81565b7352ea46506b9cc5ef470c5bf89f17dc28bb35d85c81565b630200000081565b6201000081565b73f6e2d7f616b67e46d708e4410746e9aab3a4c51881565b73818e6fecd516ecc3849daf6845e3ec868087b75581565b61010081565b846001600160a01b0316866001600160a01b03161415610dfd57610ff8565b610e05613dfd565b60405180610180016040528061103e81526020016112bf815260200161144a8152602001611467815260200161174081526020016118cb8152602001611a9c8152602001611cc18152602001611ef0815260200161211f81526020016122bd81526020016124698152509050600c83511115610eb25760405162461bcd60e51b8152600401808060200182810382526042815260200180613f196042913960600191505060405180910390fd5b600080805b8551811015610f10576000868281518110610ece57fe5b60200260200101511115610f0857610f02868281518110610eeb57fe5b6020026020010151846125d590919063ffffffff16565b92508091505b600101610eb7565b5060008211610f505760405162461bcd60e51b815260040180806020018281038252602f815260200180613e49602f913960400191505060405180910390fd5b8660005b8651811015610ff257868181518110610f6957fe5b602002602001015160001415610f7e57610fea565b6000610fb685610faa8a8581518110610f9357fe5b60200260200101518d61263890919063ffffffff16565b9063ffffffff61269116565b905083821415610fc35750815b8083039250610fe78c8c838986600c8110610fda57fe5b602002015163ffffffff16565b50505b600101610f54565b50505050505b505050505050565b600080516020613e2983398151915281565b600481565b733d9819210a31b4961b30ef54be2aed79b9c9cd3b81565b6000546001600160a01b031681565b6000816110536001600160a01b0386166126d3565b61118357604080516303795fb160e11b81526001600160a01b0387166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156110b557600080fd5b505afa1580156110c9573d6000803e3d6000fd5b505050506040513d60208110156110df57600080fd5b505190506001600160a01b03811615611181576110fc868261270f565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561115257600080fd5b505af1158015611166573d6000803e3d6000fd5b505050506040513d602081101561117c57600080fd5b505191505b505b611195846001600160a01b03166126d3565b6112b557604080516303795fb160e11b81526001600160a01b0386166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156111f757600080fd5b505afa15801561120b573d6000803e3d6000fd5b505050506040513d602081101561122157600080fd5b505190506001600160a01b038116156112b357806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b15801561128357600080fd5b505af1158015611297573d6000803e3d6000fd5b50505050506040513d60208110156112ae57600080fd5b505191505b505b90505b9392505050565b60006112df8473818e6fecd516ecc3849daf6845e3ec868087b75561270f565b73818e6fecd516ecc3849daf6845e3ec868087b7556329589f6161130b6001600160a01b0387166126d3565b611316576000611318565b835b61132a876001600160a01b03166126d3565b611334578661134a565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b8561135d886001600160a01b03166126d3565b611367578761137d565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b15801561141557600080fd5b505af1158015611429573d6000803e3d6000fd5b50505050506040513d602081101561144057600080fd5b5051949350505050565b6000806114588585856127c8565b9050600081116112b557600080fd5b600061147b846001600160a01b03166126d3565b156114e95773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156114cf57600080fd5b505af11580156114e3573d6000803e3d6000fd5b50505050505b6115386114fe856001600160a01b03166126d3565b611508578461151e565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b73794e6e91555438afc3ccf1c5076a74f42133d08d61270f565b600073794e6e91555438afc3ccf1c5076a74f42133d08d630621b4f66115666001600160a01b0388166126d3565b6115705786611586565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b85611599886001600160a01b03166126d3565b6115a357876115b9565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b15801561161757600080fd5b505af115801561162b573d6000803e3d6000fd5b505050506040513d602081101561164157600080fd5b505190506116576001600160a01b0385166126d3565b156112b557604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b1580156116b557600080fd5b505afa1580156116c9573d6000803e3d6000fd5b505050506040513d60208110156116df57600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561171f57600080fd5b505af1158015611733573d6000803e3d6000fd5b5050505090509392505050565b6000806001600160a01b038516600080516020613e998339815191521461176857600061176b565b60025b6001600160a01b038616600080516020613e2983398151915214611790576000611793565b60015b0160ff1690506000600080516020613e998339815191526001600160a01b038616146117c05760006117c3565b60025b6001600160a01b038616600080516020613e29833981519152146117e85760006117eb565b60015b0160ff16905081600f0b60001480611806575080600f0b6000145b15611816576000925050506112b8565b6118348673a2b47e3d5c44877cca798226b7b8118f9bfb7a5661270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a2b47e3d5c44877cca798226b7b8118f9bfb7a569263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b505af11580156118be573d6000803e3d6000fd5b5050505050509392505050565b6000806001600160a01b03851673dac17f958d2ee523a2206206994597c13d831ec7146118f95760006118fc565b60035b6001600160a01b038616600080516020613e9983398151915214611921576000611924565b60025b6001600160a01b038716600080516020613e298339815191521461194957600061194c565b60015b010160ff169050600073dac17f958d2ee523a2206206994597c13d831ec76001600160a01b0316856001600160a01b03161461198957600061198c565b60035b6001600160a01b038616600080516020613e99833981519152146119b15760006119b4565b60025b6001600160a01b038716600080516020613e29833981519152146119d95760006119dc565b60015b010160ff16905081600f0b600014806119f8575080600f0b6000145b15611a08576000925050506112b8565b611a26867352ea46506b9cc5ef470c5bf89f17dc28bb35d85c61270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517352ea46506b9cc5ef470c5bf89f17dc28bb35d85c9263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000806001600160a01b0385166e085d4780b73119b644ae5ecd22b37614611ac5576000611ac8565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611af3576000611af6565b60035b6001600160a01b038716600080516020613e9983398151915214611b1b576000611b1e565b60025b6001600160a01b038816600080516020613e2983398151915214611b43576000611b46565b60015b01010160ff16905060006e085d4780b73119b644ae5ecd22b3766001600160a01b0316856001600160a01b031614611b7f576000611b82565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611bad576000611bb0565b60035b6001600160a01b038716600080516020613e9983398151915214611bd5576000611bd8565b60025b6001600160a01b038816600080516020613e2983398151915214611bfd576000611c00565b60015b01010160ff16905081600f0b60001480611c1d575080600f0b6000145b15611c2d576000925050506112b8565b611c4b867345f783cce6b7ff23b2ab2d70e416cdb7d6055f5161270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517345f783cce6b7ff23b2ab2d70e416cdb7d6055f519263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000806001600160a01b038516734fabb145d64652a948d72533023f6e7a623c7c5314611cef576000611cf2565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611d1d576000611d20565b60035b6001600160a01b038716600080516020613e9983398151915214611d45576000611d48565b60025b6001600160a01b038816600080516020613e2983398151915214611d6d576000611d70565b60015b01010160ff1690506000734fabb145d64652a948d72533023f6e7a623c7c536001600160a01b0316856001600160a01b031614611dae576000611db1565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611ddc576000611ddf565b60035b6001600160a01b038716600080516020613e9983398151915214611e04576000611e07565b60025b6001600160a01b038816600080516020613e2983398151915214611e2c576000611e2f565b60015b01010160ff16905081600f0b60001480611e4c575080600f0b6000145b15611e5c576000925050506112b8565b611e7a867379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2761270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517379a8c46dea5ada233abaffd40f3a0a2b1e5a4f279263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000806001600160a01b0385167357ab1ec28d129707052df4df418d58a2d46d5f5114611f1e576000611f21565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611f4c576000611f4f565b60035b6001600160a01b038716600080516020613e9983398151915214611f74576000611f77565b60025b6001600160a01b038816600080516020613e2983398151915214611f9c576000611f9f565b60015b01010160ff16905060007357ab1ec28d129707052df4df418d58a2d46d5f516001600160a01b0316856001600160a01b031614611fdd576000611fe0565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec71461200b57600061200e565b60035b6001600160a01b038716600080516020613e9983398151915214612033576000612036565b60025b6001600160a01b038816600080516020613e298339815191521461205b57600061205e565b60015b01010160ff16905081600f0b6000148061207b575080600f0b6000145b1561208b576000925050506112b8565b6120a98673a5407eae9ba41422680e2e00537571bcc53efbfd61270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a5407eae9ba41422680e2e00537571bcc53efbfd9263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000612133846001600160a01b03166126d3565b6121ec57600061214285612bb6565b905061214e858261270f565b806001600160a01b031663a0712d68846040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561219457600080fd5b505af11580156121a8573d6000803e3d6000fd5b505050506040513d60208110156121be57600080fd5b506121e4905081856121df6001600160a01b0383163063ffffffff612da616565b61103e565b9150506112b8565b6121fe836001600160a01b03166126d3565b6122b357600061220d84612bb6565b9050600061221c86838661103e565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561226457600080fd5b505af1158015612278573d6000803e3d6000fd5b505050506040513d602081101561228e57600080fd5b506122aa90506001600160a01b0386163063ffffffff612da616565b925050506112b8565b5060009392505050565b60006001600160a01b038416600080516020613e2983398151915214156123a0576122fc847306af07097c9eeb7fd685c692751d5c66db49c21561270f565b60408051633b4da69f60e01b81523060048201526024810184905290517306af07097c9eeb7fd685c692751d5c66db49c21591633b4da69f91604480830192600092919082900301818387803b15801561235557600080fd5b505af1158015612369573d6000803e3d6000fd5b5061239992507306af07097c9eeb7fd685c692751d5c66db49c21591508590506121df823063ffffffff612da616565b90506112b8565b6001600160a01b038316600080516020613e2983398151915214156122b35760006123e0857306af07097c9eeb7fd685c692751d5c66db49c2158561103e565b6040805163ef693bed60e01b81523060048201526024810183905290519192507306af07097c9eeb7fd685c692751d5c66db49c2159163ef693bed9160448082019260009290919082900301818387803b15801561243d57600080fd5b505af1158015612451573d6000803e3d6000fd5b506121e4925050506001600160a01b03851630612da6565b600061247d846001600160a01b03166126d3565b61253b57600061248c85612e50565b9050612498858261270f565b60408051636968703360e11b81526001600160a01b03871660048201526024810185905261044d6044820152905173398ec7346dcd622edc5ae82352f02be94c62d1199163d2d0e06691606480830192600092919082900301818387803b15801561250257600080fd5b505af1158015612516573d6000803e3d6000fd5b505050506121e481856121df30856001600160a01b0316612da690919063ffffffff16565b61254d836001600160a01b03166126d3565b6122b357600061255c84612e50565b9050600061256b86838661103e565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156125b357600080fd5b505af11580156125c7573d6000803e3d6000fd5b5050505080925050506112b8565b60008282018381101561262f576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60008261264757506000612632565b8282028284828161265457fe5b041461262f5760405162461bcd60e51b8152600401808060200182810382526021815260200180613e786021913960400191505060405180910390fd5b600061262f83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250613273565b60006001600160a01b038216158061270757506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b90505b919050565b612721826001600160a01b03166126d3565b6127c45760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b15801561277657600080fd5b505afa15801561278a573d6000803e3d6000fd5b505050506040513d60208110156127a057600080fd5b5051901c6127c4576127c46001600160a01b0383168260001963ffffffff61331516565b5050565b60006127dc846001600160a01b03166126d3565b1561284a5773c0829421c1d260bd3cb3e0f06cfe2d52db2ce3156001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561283057600080fd5b505af1158015612844573d6000803e3d6000fd5b50505050505b60007352ae12abe5d8bd778bd5397f99ca900624cfadd46001600160a01b031663bb34534c6040518163ffffffff1660e01b815260040180806c42616e636f724e6574776f726b60981b815250602001905060206040518083038186803b1580156128b457600080fd5b505afa1580156128c8573d6000803e3d6000fd5b505050506040513d60208110156128de57600080fd5b5051905060606128ee86866133eb565b905061292b612905876001600160a01b03166126d3565b61290f5786612925565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b8361270f565b60006060836001600160a01b03166216e360856001600160a01b031663c7ba24bc905060e01b8589600160405160240180806020018481526020018360ff168152602001828103825285818151815260200191508051906020019060200280838360005b838110156129a757818101518382015260200161298f565b50505050905001945050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0383818316178352505050506040518082805190602001908083835b60208310612a165780518252601f1990920191602091820191016129f7565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038160008787f1925050503d8060008114612a79576040519150601f19603f3d011682016040523d82523d6000602084013e612a7e565b606091505b5091509150600082612a91576000612aa9565b818060200190516020811015612aa657600080fd5b50515b9050612abd886001600160a01b03166126d3565b8015612ac95750600081115b15612baa57604080516370a0823160e01b8152306004820152905173c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591632e1a7d4d9183916370a08231916024808301926020929190829003018186803b158015612b2757600080fd5b505afa158015612b3b573d6000803e3d6000fd5b505050506040513d6020811015612b5157600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015612b9157600080fd5b505af1158015612ba5573d6000803e3d6000fd5b505050505b98975050505050505050565b6000612bca826001600160a01b03166126d3565b15612bea5750734ddc2d193948926d02f9b1fe9e1daa0718270ed561270a565b6001600160a01b038216600080516020613e298339815191521415612c245750735d3a536e4d6dbd6114cc1ead35777bab948e364361270a565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415612c645750736c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e61270a565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e8621415612ca4575073158079ee67fce2f58472a96584a73c7ab9ac95c161270a565b6001600160a01b038216600080516020613e998339815191521415612cde57507339aa39c021dfbae8fac545936693ac917d5e756361270a565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415612d1e575073c11b1268c1a384e55c48c2391d8d480264a3a7f461270a565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415612d5e575073b3319f5d18bc0d84dd1b4825dcde5d5f7266d40761270a565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec71415612d9e575073f650c3d88d12db855b8bf7d11be6c55a4e07dcc961270a565b506000919050565b6000612db1836126d3565b15612dc757506001600160a01b03811631612632565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015612e1d57600080fd5b505afa158015612e31573d6000803e3d6000fd5b505050506040513d6020811015612e4757600080fd5b50519050612632565b6000612e64826001600160a01b03166126d3565b15612e845750733a3a65aab0dd2a17e3f1947ba16138cd37d08c0461270a565b6001600160a01b038216600080516020613e298339815191521415612ebe575073fc1e690f61efd961294b3e1ce3313fbd8aa4f85d61270a565b6001600160a01b038216600080516020613e998339815191521415612ef85750739ba00d6856a4edf4665bca2c2309936572473b7e61270a565b6001600160a01b0382167357ab1ec28d129707052df4df418d58a2d46d5f511415612f38575073625ae63000f46200499120b906716420bd05924061270a565b6001600160a01b038216734fabb145d64652a948d72533023f6e7a623c7c531415612f785750736ee0f7bb50a54ab5253da0667b0dc2ee526c30a861270a565b6001600160a01b0382166e085d4780b73119b644ae5ecd22b3761415612fb35750734da9b813057d04baef4e5800e36083717b4a034161270a565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec71415612ff357507371fc860f7d3a592a4a98740e39db31d25db65ae861270a565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415613033575073e1ba0fb44ccb0d11b80f92f4f8ed94ca3ff51d0061270a565b6001600160a01b03821673dd974d5c2e2928dea5f71b9825b8b646686bd20014156130735750739d91be44c06d373a8a226e1f3b146956083803eb61270a565b6001600160a01b0382167380fb784b7ed66730e8b1dbd9820afd29931aab0314156130b35750737d2d3688df45ce7c552e19c27e007673da9204b861270a565b6001600160a01b03821673514910771af9ca656af840dff83e8264ecf986ca14156130f3575073a64bd6c70cb9051f6a9ba1f163fdc07e0dfb5f8461270a565b6001600160a01b038216730f5d2fb29fb7d3cfee444a200298f468908cc94214156131335750736fce4a401b6b80ace52baaefe4421bd188e76f6f61270a565b6001600160a01b038216739f8f72aa9304c8b593d555f12ef6589cc3a579a214156131735750737deb5e830be29f91e298ba5ff1356bb7f814699861270a565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e86214156131b357507371010a9d003445ac60c4e6a7017c1e89a477b43861270a565b6001600160a01b03821673c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f14156131f3575073328c4c80bc7aca0834db37e6600a6c49e12da4de61270a565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415613233575073fc4b8ed459e00e5400be803a9bb3954234fd50e361270a565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415612d9e5750736fb0855c404e09c47c3fbca25f08d4e41f9f062f61270a565b600081836132ff5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156132c45781810151838201526020016132ac565b50505050905090810190601f1680156132f15780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161330b57fe5b0495945050505050565b61331e836126d3565b6133e6576000811180156133ac575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561337e57600080fd5b505afa158015613392573d6000803e3d6000fd5b505050506040513d60208110156133a857600080fd5b5051115b156133cc576133cc6001600160a01b03841683600063ffffffff613af516565b6133e66001600160a01b038416838363ffffffff613af516565b505050565b6060816001600160a01b0316836001600160a01b0316141561341c5750604080516000815260208101909152612632565b61342e836001600160a01b03166126d3565b1561344b5773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31592505b61345d826001600160a01b03166126d3565b1561347a5773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591505b6001600160a01b038316731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14806134c157506001600160a01b038216731f573d6fb3f13d689ff844b4ce37794d79a7ff1c145b156134ec5760408051600380825260808201909252906020820160608038833901905050905061350e565b60408051600580825260c08201909252906020820160a0803883390190505090505b6000806001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c146136d7576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b61356b6001600160a01b038b166126d3565b613575578961358b565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106136095780518252601f1990920191602091820191016135ea565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d806000811461366a576040519150601f19603f3d011682016040523d82523d6000602084013e61366f565b606091505b5091509150816136975760408051600080825260208201909252905b50945050505050612632565b8080602001905160208110156136ac57600080fd5b505193506001600160a01b0384166136d457604080516000808252602082019092529061368b565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14613895576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6137316001600160a01b038a166126d3565b61373b5788613751565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106137cf5780518252601f1990920191602091820191016137b0565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613830576040519150601f19603f3d011682016040523d82523d6000602084013e613835565b606091505b50915091508161385557604080516000808252602082019092529061368b565b80806020019051602081101561386a57600080fd5b505192506001600160a01b03831661389257604080516000808252602082019092529061368b565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156139585784836000815181106138c857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505081836001815181106138f657fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360028151811061393857fe5b6001600160a01b0390921660209283029190910190910152506126329050565b6001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156139fb57731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360008151811061399f57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505080836001815181106139cd57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050838360028151811061393857fe5b8483600081518110613a0957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508183600181518110613a3757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c83600281518110613a7957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508083600381518110613aa757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508383600481518110613ad557fe5b6001600160a01b0390921660209283029190910190910152505092915050565b801580613b7b575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b158015613b4d57600080fd5b505afa158015613b61573d6000803e3d6000fd5b505050506040513d6020811015613b7757600080fd5b5051155b613bb65760405162461bcd60e51b8152600401808060200182810382526036815260200180613ee36036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526133e6908490613c15826001600160a01b0316613dc1565b613c66576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310613ca45780518252601f199092019160209182019101613c85565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613d06576040519150601f19603f3d011682016040523d82523d6000602084013e613d0b565b606091505b509150915081613d62576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115613dbb57808060200190516020811015613d7e57600080fd5b5051613dbb5760405162461bcd60e51b815260040180806020018281038252602a815260200180613eb9602a913960400191505060405180910390fd5b50505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590613df557508115155b949350505050565b604051806101800160405280600c905b613e26815260200190600190039081613e0d5790505090565bfefe0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f73536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e63654f6e6553706c69743a20446973747269627574696f6e2061727261792073686f756c64206e6f74206578636565642072657365727665732061727261792073697a65a265627a7a72315820902ecc0ac5a33f82f02692c6d5f2c405a313ce3f3e5ddbefe65df23e2ee54cdb64736f6c63430005110032 \ No newline at end of file diff --git a/OneSplit.full.sol b/OneSplit.full.sol index a53fd98..69da12c 100644 --- a/OneSplit.full.sol +++ b/OneSplit.full.sol @@ -130,9 +130,11 @@ contract IOneSplitConsts { uint256 public constant FLAG_ENABLE_UNISWAP_CHAI = 0x200000; // Works only when ETH<>DAI or FLAG_ENABLE_MULTI_PATH_ETH uint256 public constant FLAG_ENABLE_UNISWAP_AAVE = 0x400000; // Works only when one of assets is ETH or FLAG_ENABLE_MULTI_PATH_ETH uint256 public constant FLAG_DISABLE_IDLE = 0x800000; + uint256 public constant FLAG_DISABLE_UNISWAP_POOL_TOKEN = 0x1000000; + uint256 public constant FLAG_DISABLE_BALANCER_POOL_TOKEN = 0x2000000; + uint256 public constant FLAG_DISABLE_CURVE_ZAP = 0x4000000; } - contract IOneSplit is IOneSplitConsts { function getExpectedReturn( IERC20 fromToken, @@ -344,6 +346,10 @@ interface IUniswapExchange { uint256 deadline, address tokenAddr ) external returns (uint256 tokensBought); + + function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline) external payable returns (uint256); + + function removeLiquidity(uint256 amount, uint256 min_eth, uint256 min_tokens, uint256 deadline) external returns (uint256, uint256); } // File: contracts/interface/IUniswapFactory.sol @@ -354,6 +360,8 @@ pragma solidity ^0.5.0; interface IUniswapFactory { function getExchange(IERC20 token) external view returns (IUniswapExchange exchange); + + function getToken(address exchange) external view returns (IERC20 token); } // File: contracts/interface/IKyberNetworkContract.sol @@ -530,8 +538,14 @@ interface ICurve { // solium-disable-next-line mixedcase function get_dy_underlying(int128 i, int128 j, uint256 dx) external view returns(uint256 dy); + function get_virtual_price() external view returns(uint256); + // solium-disable-next-line mixedcase function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 minDy) external; + + function coins(int128 arg0) external view returns (address); + + function balances(int128 arg0) external view returns (uint256); } // File: contracts/interface/IChai.sol @@ -996,6 +1010,13 @@ contract IOneSplitView is IOneSplitConsts { uint256 returnAmount, uint256[] memory distribution ); + + function _calculateBancorReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 flags + ) external view returns(uint256); } @@ -1254,6 +1275,13 @@ contract OneSplitViewWrapBase is IOneSplitView, OneSplitRoot { uint256 returnAmount, uint256[] memory distribution ); + + function _calculateBancorReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 flags + ) public view returns(uint256); } @@ -1282,18 +1310,18 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { } function(IERC20,IERC20,uint256,uint256) view returns(uint256)[DEXES_COUNT] memory reserves = [ - flags.check(FLAG_DISABLE_UNISWAP) ? _calculateNoReturn : calculateUniswapReturn, - flags.check(FLAG_DISABLE_KYBER) ? _calculateNoReturn : calculateKyberReturn, - flags.check(FLAG_DISABLE_BANCOR) ? _calculateNoReturn : calculateBancorReturn, - flags.check(FLAG_DISABLE_OASIS) ? _calculateNoReturn : calculateOasisReturn, - flags.check(FLAG_DISABLE_CURVE_COMPOUND) ? _calculateNoReturn : calculateCurveCompound, - flags.check(FLAG_DISABLE_CURVE_USDT) ? _calculateNoReturn : calculateCurveUsdt, - flags.check(FLAG_DISABLE_CURVE_Y) ? _calculateNoReturn : calculateCurveY, - flags.check(FLAG_DISABLE_CURVE_BINANCE) ? _calculateNoReturn : calculateCurveBinance, - flags.check(FLAG_DISABLE_CURVE_SYNTHETIX) ? _calculateNoReturn : calculateCurveSynthetix, - !flags.check(FLAG_ENABLE_UNISWAP_COMPOUND) ? _calculateNoReturn : calculateUniswapCompound, - !flags.check(FLAG_ENABLE_UNISWAP_CHAI) ? _calculateNoReturn : calculateUniswapChai, - !flags.check(FLAG_ENABLE_UNISWAP_AAVE) ? _calculateNoReturn : calculateUniswapAave + flags.check(FLAG_DISABLE_UNISWAP) ? _calculateNoReturn : _calculateUniswapReturn, + flags.check(FLAG_DISABLE_KYBER) ? _calculateNoReturn : _calculateKyberReturn, + flags.check(FLAG_DISABLE_BANCOR) ? _calculateNoReturn : _calculateBancorReturn, + flags.check(FLAG_DISABLE_OASIS) ? _calculateNoReturn : _calculateOasisReturn, + flags.check(FLAG_DISABLE_CURVE_COMPOUND) ? _calculateNoReturn : _calculateCurveCompound, + flags.check(FLAG_DISABLE_CURVE_USDT) ? _calculateNoReturn : _calculateCurveUsdt, + flags.check(FLAG_DISABLE_CURVE_Y) ? _calculateNoReturn : _calculateCurveY, + flags.check(FLAG_DISABLE_CURVE_BINANCE) ? _calculateNoReturn : _calculateCurveBinance, + flags.check(FLAG_DISABLE_CURVE_SYNTHETIX) ? _calculateNoReturn : _calculateCurveSynthetix, + !flags.check(FLAG_ENABLE_UNISWAP_COMPOUND) ? _calculateNoReturn : _calculateUniswapCompound, + !flags.check(FLAG_ENABLE_UNISWAP_CHAI) ? _calculateNoReturn : _calculateUniswapChai, + !flags.check(FLAG_ENABLE_UNISWAP_AAVE) ? _calculateNoReturn : _calculateUniswapAave ]; uint256[DEXES_COUNT] memory rates; @@ -1341,7 +1369,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { // View Helpers - function calculateCurveCompound( + function _calculateCurveCompound( IERC20 fromToken, IERC20 destToken, uint256 amount, @@ -1356,7 +1384,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return curveCompound.get_dy_underlying(i - 1, j - 1, amount); } - function calculateCurveUsdt( + function _calculateCurveUsdt( IERC20 fromToken, IERC20 destToken, uint256 amount, @@ -1375,7 +1403,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return curveUsdt.get_dy_underlying(i - 1, j - 1, amount); } - function calculateCurveY( + function _calculateCurveY( IERC20 fromToken, IERC20 destToken, uint256 amount, @@ -1396,7 +1424,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return curveY.get_dy_underlying(i - 1, j - 1, amount); } - function calculateCurveBinance( + function _calculateCurveBinance( IERC20 fromToken, IERC20 destToken, uint256 amount, @@ -1417,7 +1445,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return curveBinance.get_dy_underlying(i - 1, j - 1, amount); } - function calculateCurveSynthetix( + function _calculateCurveSynthetix( IERC20 fromToken, IERC20 destToken, uint256 amount, @@ -1438,7 +1466,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return curveSynthetix.get_dy_underlying(i - 1, j - 1, amount); } - function calculateUniswapReturn( + function _calculateUniswapReturn( IERC20 fromToken, IERC20 toToken, uint256 amount, @@ -1487,7 +1515,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return returnAmount; } - function calculateUniswapCompound( + function _calculateUniswapCompound( IERC20 fromToken, IERC20 toToken, uint256 amount, @@ -1500,7 +1528,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { if (!fromToken.isETH()) { ICompoundToken fromCompound = _getCompoundToken(fromToken); if (fromCompound != ICompoundToken(0)) { - return calculateUniswapReturn( + return _calculateUniswapReturn( fromCompound, toToken, amount.mul(1e18).div(fromCompound.exchangeRateStored()), @@ -1510,7 +1538,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { } else { ICompoundToken toCompound = _getCompoundToken(toToken); if (toCompound != ICompoundToken(0)) { - return calculateUniswapReturn( + return _calculateUniswapReturn( fromToken, toCompound, amount, @@ -1522,14 +1550,14 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return 0; } - function calculateUniswapChai( + function _calculateUniswapChai( IERC20 fromToken, IERC20 toToken, uint256 amount, uint256 flags ) public view returns(uint256) { if (fromToken == dai && toToken.isETH()) { - return calculateUniswapReturn( + return _calculateUniswapReturn( chai, toToken, chai.daiToChai(amount), @@ -1538,7 +1566,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { } if (fromToken.isETH() && toToken == dai) { - return chai.chaiToDai(calculateUniswapReturn( + return chai.chaiToDai(_calculateUniswapReturn( fromToken, chai, amount, @@ -1549,7 +1577,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return 0; } - function calculateUniswapAave( + function _calculateUniswapAave( IERC20 fromToken, IERC20 toToken, uint256 amount, @@ -1562,7 +1590,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { if (!fromToken.isETH()) { IAaveToken fromAave = _getAaveToken(fromToken); if (fromAave != IAaveToken(0)) { - return calculateUniswapReturn( + return _calculateUniswapReturn( fromAave, toToken, amount, @@ -1572,7 +1600,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { } else { IAaveToken toAave = _getAaveToken(toToken); if (toAave != IAaveToken(0)) { - return calculateUniswapReturn( + return _calculateUniswapReturn( fromToken, toAave, amount, @@ -1584,7 +1612,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return 0; } - function calculateKyberReturn( + function _calculateKyberReturn( IERC20 fromToken, IERC20 toToken, uint256 amount, @@ -1677,7 +1705,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { .div(1e18); } - function calculateBancorReturn( + function _calculateBancorReturn( IERC20 fromToken, IERC20 toToken, uint256 amount, @@ -1701,7 +1729,7 @@ contract OneSplitView is IOneSplitView, OneSplitRoot { return returnAmount; } - function calculateOasisReturn( + function _calculateOasisReturn( IERC20 fromToken, IERC20 toToken, uint256 amount, @@ -1761,6 +1789,12 @@ contract OneSplitBaseWrap is IOneSplit, OneSplitRoot { uint256[] memory distribution, uint256 /*flags*/ // See constants in IOneSplit.sol ) internal; + + function _swapOnBancorSafe( + IERC20 fromToken, + IERC20 toToken, + uint256 amount + ) external returns(uint256); } @@ -2065,6 +2099,20 @@ contract OneSplit is IOneSplit, OneSplitRoot { IERC20 fromToken, IERC20 toToken, uint256 amount + ) internal returns(uint256) { + uint256 ret = _swapOnBancorSafe( + fromToken, + toToken, + amount + ); + require(ret > 0); + return ret; + } + + function _swapOnBancorSafe( + IERC20 fromToken, + IERC20 toToken, + uint256 amount ) internal returns(uint256) { if (fromToken.isETH()) { bancorEtherToken.deposit.value(amount)(); @@ -2074,9 +2122,18 @@ contract OneSplit is IOneSplit, OneSplitRoot { address[] memory path = _buildBancorPath(fromToken, toToken); _infiniteApproveIfNeeded(fromToken.isETH() ? bancorEtherToken : fromToken, address(bancorNetwork)); - uint256 returnAmount = bancorNetwork.claimAndConvert(path, amount, 1); + (bool success, bytes memory data) = address(bancorNetwork).call.gas(1500000)( + abi.encodeWithSelector( + bancorNetwork.claimAndConvert.selector, + path, + amount, + 1 + ) + ); - if (toToken.isETH()) { + uint256 returnAmount = success ? abi.decode(data, (uint256)) : 0; + + if (toToken.isETH() && returnAmount > 0) { bancorEtherToken.withdraw(bancorEtherToken.balanceOf(address(this))); } @@ -3780,206 +3837,174 @@ contract OneSplitWeth is OneSplitBaseWrap { } } -// File: contracts/interface/ISmartTokenConverter.sol +// File: contracts/interface/IBFactory.sol pragma solidity ^0.5.0; -//pragma experimental ABIEncoderV2; - - -interface ISmartTokenConverter { - - struct Reserve { - uint256 virtualBalance; // reserve virtual balance - uint32 ratio; // reserve ratio, represented in ppm, 1-1000000 - bool isVirtualBalanceEnabled; // true if virtual balance is enabled, false if not - bool isSaleEnabled; // is sale of the reserve token enabled, can be set by the owner - bool isSet; // used to tell if the mapping element is defined - } - - function version() external view returns (uint16); - - function reserves(address) external view returns (Reserve memory); - - function getReserveRatio(IERC20 token) external view returns (uint256); - - function connectorTokenCount() external view returns (uint256); - - function connectorTokens(uint256 i) external view returns (IERC20); - - function liquidate(uint256 _amount) external; - - function fund(uint256 _amount) external; - - function convert2(IERC20 _fromToken, IERC20 _toToken, uint256 _amount, uint256 _minReturn, address _affiliateAccount, uint256 _affiliateFee) external returns (uint256); - - function convert(IERC20 _fromToken, IERC20 _toToken, uint256 _amount, uint256 _minReturn) external returns (uint256); +interface IBFactory { + function isBPool(address b) external view returns (bool); } -// File: contracts/interface/ISmartToken.sol +// File: contracts/interface/IBPool.sol pragma solidity ^0.5.0; - - -interface ISmartToken { - function owner() external view returns (ISmartTokenConverter); +contract BConst { + uint public constant EXIT_FEE = 0; } -// File: contracts/interface/ISmartTokenRegistry.sol - -pragma solidity ^0.5.0; +contract IBMath is BConst { + function calcPoolOutGivenSingleIn( + uint tokenBalanceIn, + uint tokenWeightIn, + uint poolSupply, + uint totalWeight, + uint tokenAmountIn, + uint swapFee + ) + public + pure returns (uint poolAmountOut); +} +contract IBPool is IERC20, IBMath { + function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn) external; + function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut) external; -interface ISmartTokenRegistry { - function isSmartToken(IERC20 token) external view returns (bool); -} + function joinswapExternAmountIn(address tokenIn, uint tokenAmountIn, uint minPoolAmountOut) external returns (uint poolAmountOut); -// File: contracts/interface/ISmartTokenFormula.sol + function getCurrentTokens() external view returns (address[] memory tokens); -pragma solidity ^0.5.0; + function getBalance(address token) external view returns (uint); + function getNormalizedWeight(address token) external view returns (uint); + function getDenormalizedWeight(address token) external view returns (uint); -interface ISmartTokenFormula { - function calculateLiquidateReturn( - uint256 supply, - uint256 reserveBalance, - uint32 totalRatio, - uint256 amount - ) external view returns (uint256); + function getTotalDenormalizedWeight() external view returns (uint); - function calculatePurchaseReturn( - uint256 supply, - uint256 reserveBalance, - uint32 totalRatio, - uint256 amount - ) external view returns (uint256); + function getSwapFee() external view returns (uint); } -// File: contracts/OneSplitSmartToken.sol +// File: contracts/OneSplitBalancerPoolToken.sol pragma solidity ^0.5.0; -//pragma experimental ABIEncoderV2; - -contract OneSplitSmartTokenBase { +contract OneSplitBalancerPoolTokenBase { using SafeMath for uint256; - ISmartTokenRegistry smartTokenRegistry = ISmartTokenRegistry(0xf6E2D7F616B67E46D708e4410746E9AAb3a4C518); - ISmartTokenFormula smartTokenFormula = ISmartTokenFormula(0x524619EB9b4cdFFa7DA13029b33f24635478AFc0); + // todo: factory for Bronze release + // may be changed in future + IBFactory bFactory = IBFactory(0x9424B1412450D0f8Fc2255FAf6046b98213B76Bd); - struct TokenWithRatio { + struct TokenWithWeight { IERC20 token; - uint256 ratio; - } - - struct SmartTokenDetails { - TokenWithRatio[] reserveTokenList; - address converter; - uint256 totalReserveTokensRatio; - } - - function _getSmartTokenDetails(ISmartToken smartToken) internal view returns (SmartTokenDetails memory details) { - ISmartTokenConverter converter = smartToken.owner(); - (TokenWithRatio[] memory reserveTokenList, uint256 totalReserveTokensRatio) = _getTokens(converter); - - details.reserveTokenList = reserveTokenList; - details.converter = address(converter); - details.totalReserveTokensRatio = totalReserveTokensRatio; - - return details; + uint256 reserveBalance; + uint256 denormalizedWeight; } - function _getTokens( - ISmartTokenConverter converter - ) - internal - view - returns(TokenWithRatio[] memory reserveTokenList, uint256 totalRatio) - { - reserveTokenList = new TokenWithRatio[](converter.connectorTokenCount()); - for (uint256 i = 0; i < reserveTokenList.length; i++) { - reserveTokenList[i].token = converter.connectorTokens(i); - reserveTokenList[i].ratio = _getReserveRatio(converter, reserveTokenList[i].token); - totalRatio = totalRatio.add(reserveTokenList[i].ratio); - } - return (reserveTokenList, totalRatio); + struct PoolTokenDetails { + TokenWithWeight[] tokens; + uint256 totalWeight; + uint256 totalSupply; } - function _getReserveRatio( - ISmartTokenConverter converter, - IERC20 token - ) + function _getPoolDetails(IBPool poolToken) internal view - returns (uint256) + returns(PoolTokenDetails memory details) { - if (converter.version() >= 22) { - return converter.getReserveRatio(token); + address[] memory currentTokens = poolToken.getCurrentTokens(); + details.tokens = new TokenWithWeight[](currentTokens.length); + details.totalWeight = poolToken.getTotalDenormalizedWeight(); + details.totalSupply = poolToken.totalSupply(); + for (uint256 i = 0; i < details.tokens.length; i++) { + details.tokens[i].token = IERC20(currentTokens[i]); + details.tokens[i].denormalizedWeight = poolToken.getDenormalizedWeight(currentTokens[i]); + details.tokens[i].reserveBalance = poolToken.getBalance(currentTokens[i]); } - - return uint256(converter.reserves(address(token)).ratio); - } - - function _calcExchangeAmount(uint256 amount, uint256 ratio, uint256 totalRatio) internal pure returns (uint256) { - return amount.mul(ratio).div(totalRatio); } } - -contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { +contract OneSplitBalancerPoolTokenView is OneSplitViewWrapBase, OneSplitBalancerPoolTokenBase { function getExpectedReturn( IERC20 fromToken, IERC20 toToken, uint256 amount, uint256 parts, - uint256 disableFlags + uint256 flags ) public view - returns( - uint256, - uint256[] memory + returns ( + uint256 returnAmount, + uint256[] memory distribution ) { - if (fromToken == toToken) { - return (amount, new uint256[](9)); + return (amount, new uint256[](DEXES_COUNT)); } - if (!disableFlags.check(FLAG_DISABLE_SMART_TOKEN)) { - if (smartTokenRegistry.isSmartToken(fromToken)) { + if (!flags.check(FLAG_DISABLE_BALANCER_POOL_TOKEN)) { + bool isPoolTokenFrom = bFactory.isBPool(address(fromToken)); + bool isPoolTokenTo = bFactory.isBPool(address(toToken)); - return _getExpectedReturnFromSmartToken( + if (isPoolTokenFrom && isPoolTokenTo) { + ( + uint256 returnETHAmount, + uint256[] memory poolTokenFromDistribution + ) = _getExpectedReturnFromBalancerPoolToken( fromToken, - toToken, + ETH_ADDRESS, amount, parts, - disableFlags + FLAG_DISABLE_BALANCER_POOL_TOKEN ); - } + ( + uint256 returnPoolTokenToAmount, + uint256[] memory poolTokenToDistribution + ) = _getExpectedReturnToBalancerPoolToken( + ETH_ADDRESS, + toToken, + returnETHAmount, + parts, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + + for (uint i = 0; i < poolTokenToDistribution.length; i++) { + poolTokenFromDistribution[i] |= poolTokenToDistribution[i] << 128; + } - if (smartTokenRegistry.isSmartToken(toToken)) { + return (returnPoolTokenToAmount, poolTokenFromDistribution); + } - return _getExpectedReturnToSmartToken( + if (isPoolTokenFrom) { + return _getExpectedReturnFromBalancerPoolToken( fromToken, toToken, amount, parts, - disableFlags + FLAG_DISABLE_BALANCER_POOL_TOKEN ); + } + if (isPoolTokenTo) { + return _getExpectedReturnToBalancerPoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); } } @@ -3988,120 +4013,135 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { toToken, amount, parts, - disableFlags + flags ); } - function _getExpectedReturnFromSmartToken( - IERC20 fromToken, + function _getExpectedReturnFromBalancerPoolToken( + IERC20 poolToken, IERC20 toToken, uint256 amount, uint256 parts, - uint256 disableFlags + uint256 flags ) private view - returns( + returns ( uint256 returnAmount, uint256[] memory distribution ) { - distribution = new uint256[](9); + distribution = new uint256[](DEXES_COUNT); - SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(fromToken))); + IBPool bToken = IBPool(address(poolToken)); + address[] memory currentTokens = bToken.getCurrentTokens(); - for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { - uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( - fromToken.totalSupply(), - smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter), - uint32(smartTokenDetails.totalReserveTokensRatio), - amount - ); + uint256 pAiAfterExitFee = amount.sub( + amount.mul(bToken.EXIT_FEE()) + ); + uint256 ratio = pAiAfterExitFee.mul(1e18).div(poolToken.totalSupply()); + for (uint i = 0; i < currentTokens.length; i++) { + uint256 tokenAmountOut = bToken.getBalance(currentTokens[i]).mul(ratio).div(1e18); + + if (currentTokens[i] == address(toToken)) { + returnAmount = returnAmount.add(tokenAmountOut); + continue; + } - (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( - smartTokenDetails.reserveTokenList[i].token, + (uint256 ret, uint256[] memory dist) = getExpectedReturn( + IERC20(currentTokens[i]), toToken, - srcAmount, + tokenAmountOut, parts, - disableFlags + flags ); returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { - distribution[j] = distribution[j].add(dist[j] << (i * 8)); + distribution[j] |= dist[j] << (i * 8); } } + return (returnAmount, distribution); } - function _getExpectedReturnToSmartToken( + function _getExpectedReturnToBalancerPoolToken( IERC20 fromToken, - IERC20 toToken, + IERC20 poolToken, uint256 amount, uint256 parts, - uint256 disableFlags + uint256 flags ) private view - returns( + returns ( uint256 minFundAmount, uint256[] memory distribution ) { - distribution = new uint256[](9); + distribution = new uint256[](DEXES_COUNT); minFundAmount = uint256(-1); - SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(toToken))); + PoolTokenDetails memory details = _getPoolDetails(IBPool(address(poolToken))); - uint256[] memory fundAmounts = new uint256[](smartTokenDetails.reserveTokenList.length); - for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + uint256[] memory tokenAmounts = new uint256[](details.tokens.length); + uint256[] memory dist; + uint256[] memory fundAmounts = new uint256[](details.tokens.length); - uint256 exchangeAmount = _calcExchangeAmount( - amount, - smartTokenDetails.reserveTokenList[i].ratio, - smartTokenDetails.totalReserveTokensRatio - ); + for (uint i = 0; i < details.tokens.length; i++) { + uint256 exchangeAmount = amount.mul( + details.tokens[i].denormalizedWeight + ).div(details.totalWeight); - (uint256 tokenAmount, uint256[] memory dist) = super.getExpectedReturn( - fromToken, - smartTokenDetails.reserveTokenList[i].token, - exchangeAmount, - parts, - disableFlags | FLAG_DISABLE_BANCOR - ); + if (details.tokens[i].token != fromToken) { + (tokenAmounts[i], dist) = getExpectedReturn( + fromToken, + details.tokens[i].token, + exchangeAmount, + parts, + flags + ); - for (uint j = 0; j < distribution.length; j++) { - distribution[j] = distribution[j].add(dist[j] << (i * 8)); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } else { + tokenAmounts[i] = exchangeAmount; } - fundAmounts[i] = toToken.totalSupply() - .mul(tokenAmount) - .div(smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter)); + fundAmounts[i] = tokenAmounts[i] + .mul(details.totalSupply) + .div(details.tokens[i].reserveBalance); if (fundAmounts[i] < minFundAmount) { minFundAmount = fundAmounts[i]; } } - // Swap leftovers for SmartToken - for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { - uint256 reserveBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter); - - uint256 leftover = fundAmounts[i].sub(minFundAmount) - .mul(reserveBalance) - .div(toToken.totalSupply()); - - if (leftover > 0) { - minFundAmount = minFundAmount.add( - smartTokenFormula.calculatePurchaseReturn( - toToken.totalSupply(), - reserveBalance, - uint32(smartTokenDetails.totalReserveTokensRatio), - leftover - ) - ); - } - } +// uint256 _minFundAmount = minFundAmount; +// uint256 swapFee = IBPool(address(poolToken)).getSwapFee(); + // Swap leftovers for PoolToken +// for (uint i = 0; i < details.tokens.length; i++) { +// if (_minFundAmount == fundAmounts[i]) { +// continue; +// } +// +// uint256 leftover = tokenAmounts[i].sub( +// fundAmounts[i].mul(details.tokens[i].reserveBalance).div(details.totalSupply) +// ); +// +// uint256 tokenRet = IBPool(address(poolToken)).calcPoolOutGivenSingleIn( +// details.tokens[i].reserveBalance, +// details.tokens[i].denormalizedWeight, +// details.totalSupply, +// details.totalWeight, +// leftover, +// swapFee +// ); +// +// minFundAmount = minFundAmount.add(tokenRet); +// } return (minFundAmount, distribution); } @@ -4109,47 +4149,71 @@ contract OneSplitSmartTokenView is OneSplitBaseView, OneSplitSmartTokenBase { } -contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { - - // todo: think about smart tokens with one token in reserve - // todo: think about the case when toToken == reserveToken - // todo: think about the case when fromToken == reserveToken - // todo: think about the case when fromToken == smartToken1, toToken == smartToken2 +contract OneSplitBalancerPoolToken is OneSplitBaseWrap, OneSplitBalancerPoolTokenBase { function _swap( IERC20 fromToken, IERC20 toToken, uint256 amount, uint256[] memory distribution, - uint256 disableFlags + uint256 flags ) internal { if (fromToken == toToken) { return; } - if (!disableFlags.check(FLAG_DISABLE_SMART_TOKEN)) { + if (!flags.check(FLAG_DISABLE_BALANCER_POOL_TOKEN)) { + bool isPoolTokenFrom = bFactory.isBPool(address(fromToken)); + bool isPoolTokenTo = bFactory.isBPool(address(toToken)); + + if (isPoolTokenFrom && isPoolTokenTo) { + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] & ((1 << 128) - 1); + } - if (smartTokenRegistry.isSmartToken(fromToken)) { + uint256 ethBalanceBefore = address(this).balance; - return _swapFromSmartToken( + _swapFromBalancerPoolToken( fromToken, - toToken, + ETH_ADDRESS, amount, - distribution, - disableFlags + dist, + FLAG_DISABLE_BALANCER_POOL_TOKEN ); - } + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] >> 128; + } - if (smartTokenRegistry.isSmartToken(toToken)) { + uint256 ethBalanceAfter = address(this).balance; - return _swapToSmartToken( + return _swapToBalancerPoolToken( + ETH_ADDRESS, + toToken, + ethBalanceAfter.sub(ethBalanceBefore), + dist, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + } + + if (isPoolTokenFrom) { + return _swapFromBalancerPoolToken( fromToken, toToken, amount, distribution, - disableFlags + FLAG_DISABLE_BALANCER_POOL_TOKEN ); + } + if (isPoolTokenTo) { + return _swapToBalancerPoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); } } @@ -4158,155 +4222,1638 @@ contract OneSplitSmartToken is OneSplitBase, OneSplitSmartTokenBase { toToken, amount, distribution, - disableFlags + flags ); } - function _swapFromSmartToken( - IERC20 fromToken, + function _swapFromBalancerPoolToken( + IERC20 poolToken, IERC20 toToken, uint256 amount, uint256[] memory distribution, - uint256 disableFlags + uint256 flags ) private { - SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(fromToken))); + IBPool bToken = IBPool(address(poolToken)); - uint256[] memory tokenBalanceBefore = new uint256[](smartTokenDetails.reserveTokenList.length); - uint256[][] memory dist = new uint256[][](smartTokenDetails.reserveTokenList.length); - for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { - dist[i] = new uint256[](distribution.length); + address[] memory currentTokens = bToken.getCurrentTokens(); - tokenBalanceBefore[i] = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + uint256 ratio = amount.sub( + amount.mul(bToken.EXIT_FEE()) + ).mul(1e18).div(poolToken.totalSupply()); - for (uint j = 0; j < distribution.length; j++) { - dist[i][j] = (distribution[j] >> (i * 8)) & 0xFF; - } + uint256[] memory minAmountsOut = new uint256[](currentTokens.length); + for (uint i = 0; i < currentTokens.length; i++) { + minAmountsOut[i] = bToken.getBalance(currentTokens[i]).mul(ratio).div(1e18).mul(995).div(1000); // 0.5% slippage; } - ISmartTokenConverter(smartTokenDetails.converter).liquidate(amount); + bToken.exitPool(amount, minAmountsOut); + + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < currentTokens.length; i++) { + + if (currentTokens[i] == address(toToken)) { + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } - for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { - uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + uint256 exchangeTokenAmount = IERC20(currentTokens[i]).balanceOf(address(this)); - return super._swap( - smartTokenDetails.reserveTokenList[i].token, + this.swap( + IERC20(currentTokens[i]), toToken, - tokenBalanceAfter.sub(tokenBalanceBefore[i]), - dist[i], - disableFlags + exchangeTokenAmount, + 0, + dist, + flags ); } } - function _swapToSmartToken( + function _swapToBalancerPoolToken( IERC20 fromToken, - IERC20 toToken, + IERC20 poolToken, uint256 amount, uint256[] memory distribution, - uint256 disableFlags + uint256 flags ) private { - + uint256[] memory dist = new uint256[](distribution.length); uint256 minFundAmount = uint256(-1); - SmartTokenDetails memory smartTokenDetails = _getSmartTokenDetails(ISmartToken(address(toToken))); - - uint256[] memory fundAmounts = new uint256[](smartTokenDetails.reserveTokenList.length); - for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { + PoolTokenDetails memory details = _getPoolDetails(IBPool(address(poolToken))); - uint256 exchangeAmount = _calcExchangeAmount( - amount, - smartTokenDetails.reserveTokenList[i].ratio, - smartTokenDetails.totalReserveTokensRatio - ); + uint256[] memory maxAmountsIn = new uint256[](details.tokens.length); + uint256 curFundAmount; + for (uint i = 0; i < details.tokens.length; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].denormalizedWeight) + .div(details.totalWeight); - uint256 tokenBalanceBefore = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + if (details.tokens[i].token != fromToken) { + uint256 tokenBalanceBefore = details.tokens[i].token.balanceOf(address(this)); - for (uint j = 0; j < distribution.length; j++) { - distribution[j] = (distribution[j] >> (i * 8)) & 0xFF; - } + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } - super._swap( - fromToken, - smartTokenDetails.reserveTokenList[i].token, - exchangeAmount, - distribution, - disableFlags - ); + this.swap( + fromToken, + details.tokens[i].token, + exchangeAmount, + 0, + dist, + flags + ); - uint256 tokenBalanceAfter = smartTokenDetails.reserveTokenList[i].token.balanceOf(msg.sender); + uint256 tokenBalanceAfter = details.tokens[i].token.balanceOf(address(this)); - fundAmounts[i] = toToken.totalSupply() - .mul(tokenBalanceAfter.sub(tokenBalanceBefore)) - .div(smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter)); + curFundAmount = ( + tokenBalanceAfter.sub(tokenBalanceBefore) + ).mul(details.totalSupply).div(details.tokens[i].reserveBalance); + } else { + curFundAmount = ( + exchangeAmount + ).mul(details.totalSupply).div(details.tokens[i].reserveBalance); + } - if (fundAmounts[i] < minFundAmount) { - minFundAmount = fundAmounts[i]; + if (curFundAmount < minFundAmount) { + minFundAmount = curFundAmount; } - _infiniteApproveIfNeeded(smartTokenDetails.reserveTokenList[i].token, smartTokenDetails.converter); + maxAmountsIn[i] = uint256(-1); + _infiniteApproveIfNeeded(details.tokens[i].token, address(poolToken)); } - ISmartTokenConverter(smartTokenDetails.converter).fund(minFundAmount); + // todo: check for vulnerability + IBPool(address(poolToken)).joinPool(minFundAmount, maxAmountsIn); - // Swap leftovers for SmartToken - for (uint16 i = 0; i < smartTokenDetails.reserveTokenList.length; i++) { - uint256 reserveBalance = smartTokenDetails.reserveTokenList[i].token.balanceOf(smartTokenDetails.converter); + // Return leftovers + for (uint i = 0; i < details.tokens.length; i++) { + details.tokens[i].token.universalTransfer(msg.sender, details.tokens[i].token.balanceOf(address(this))); + } + } +} - uint256 leftover = fundAmounts[i].sub(minFundAmount) - .mul(reserveBalance) - .div(toToken.totalSupply()); +// File: contracts/OneSplitUniswapPoolToken.sol - if (leftover > 0) { +pragma solidity ^0.5.0; - convert( - ISmartTokenConverter(smartTokenDetails.converter), - smartTokenDetails.reserveTokenList[i].token, - toToken, - leftover - ); + + + +contract OneSplitUniswapPoolTokenBase { + using SafeMath for uint256; + + IUniswapFactory uniswapFactory = IUniswapFactory(0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95); + + function isLiquidityPool(IERC20 token) internal view returns (bool) { + return address(uniswapFactory.getToken(address(token))) != address(0); + } + + function getMaxPossibleFund( + IERC20 poolToken, + IERC20 uniswapToken, + uint256 tokenAmount, + uint256 existEthAmount + ) + internal + view + returns ( + uint256, + uint256 + ) + { + uint256 ethReserve = address(poolToken).balance; + uint256 totalLiquidity = poolToken.totalSupply(); + uint256 tokenReserve = uniswapToken.balanceOf(address(poolToken)); + + uint256 possibleEthAmount = ethReserve.mul( + tokenAmount.sub(1) + ).div(tokenReserve); + + if (existEthAmount > possibleEthAmount) { + return ( + possibleEthAmount, + possibleEthAmount.mul(totalLiquidity).div(ethReserve) + ); + } + + return ( + existEthAmount, + existEthAmount.mul(totalLiquidity).div(ethReserve) + ); + } + +} + +contract OneSplitUniswapPoolTokenView is OneSplitViewWrapBase, OneSplitUniswapPoolTokenBase { + + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + public + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + if (fromToken == toToken) { + return (amount, new uint256[](DEXES_COUNT)); + } + + + if (!flags.check(FLAG_DISABLE_UNISWAP_POOL_TOKEN)) { + bool isPoolTokenFrom = isLiquidityPool(fromToken); + bool isPoolTokenTo = isLiquidityPool(toToken); + + if (isPoolTokenFrom && isPoolTokenTo) { + ( + uint256 returnETHAmount, + uint256[] memory poolTokenFromDistribution + ) = _getExpectedReturnFromPoolToken( + fromToken, + ETH_ADDRESS, + amount, + parts, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + + ( + uint256 returnPoolTokenToAmount, + uint256[] memory poolTokenToDistribution + ) = _getExpectedReturnToPoolToken( + ETH_ADDRESS, + toToken, + returnETHAmount, + parts, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + + for (uint i = 0; i < poolTokenToDistribution.length; i++) { + poolTokenFromDistribution[i] |= poolTokenToDistribution[i] << 128; + } + + return (returnPoolTokenToAmount, poolTokenFromDistribution); + } + + if (isPoolTokenFrom) { + return _getExpectedReturnFromPoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _getExpectedReturnToPoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + } + + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + flags + ); + } + + function _getExpectedReturnFromPoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + + distribution = new uint256[](DEXES_COUNT); + + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + + uint256 totalSupply = poolToken.totalSupply(); + + uint256 ethReserve = address(poolToken).balance; + uint256 ethAmount = amount.mul(ethReserve).div(totalSupply); + + if (!toToken.isETH()) { + (uint256 ret, uint256[] memory dist) = getExpectedReturn( + ETH_ADDRESS, + toToken, + ethAmount, + parts, + flags + ); + + returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j]; + } + } else { + returnAmount = returnAmount.add(ethAmount); + } + + uint256 tokenReserve = uniswapToken.balanceOf(address(poolToken)); + uint256 exchangeTokenAmount = amount.mul(tokenReserve).div(totalSupply); + + if (toToken != uniswapToken) { + (uint256 ret, uint256[] memory dist) = getExpectedReturn( + uniswapToken, + toToken, + exchangeTokenAmount, + parts, + flags + ); + + returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << 8; + } + } else { + returnAmount = returnAmount.add(exchangeTokenAmount); + } + + return (returnAmount, distribution); + } + + function _getExpectedReturnToPoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + + distribution = new uint256[](DEXES_COUNT); + + uint256[] memory dist = new uint256[](DEXES_COUNT); + + uint256 ethAmount; + uint256 partAmountForEth = amount.div(2); + if (!fromToken.isETH()) { + (ethAmount, dist) = super.getExpectedReturn( + fromToken, + ETH_ADDRESS, + partAmountForEth, + parts, + flags + ); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j]; + } + } else { + ethAmount = partAmountForEth; + } + + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + + uint256 tokenAmount; + uint256 partAmountForToken = amount.sub(partAmountForEth); + if (fromToken != uniswapToken) { + (tokenAmount, dist) = super.getExpectedReturn( + fromToken, + uniswapToken, + partAmountForToken, + parts, + flags + ); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << 8; + } + } else { + tokenAmount = partAmountForToken; + } + + (, returnAmount) = getMaxPossibleFund( + poolToken, + uniswapToken, + tokenAmount, + ethAmount + ); + + return ( + returnAmount, + distribution + ); + } + +} + + +contract OneSplitUniswapPoolToken is OneSplitBaseWrap, OneSplitUniswapPoolTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) internal { + if (fromToken == toToken) { + return; + } + + if (!flags.check(FLAG_DISABLE_UNISWAP_POOL_TOKEN)) { + bool isPoolTokenFrom = isLiquidityPool(fromToken); + bool isPoolTokenTo = isLiquidityPool(toToken); + + if (isPoolTokenFrom && isPoolTokenTo) { + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] & ((1 << 128) - 1); + } + + uint256 ethBalanceBefore = address(this).balance; + + _swapFromPoolToken( + fromToken, + ETH_ADDRESS, + amount, + dist, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] >> 128; + } + + uint256 ethBalanceAfter = address(this).balance; + + return _swapToPoolToken( + ETH_ADDRESS, + toToken, + ethBalanceAfter.sub(ethBalanceBefore), + dist, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + + if (isPoolTokenFrom) { + return _swapFromPoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _swapToPoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + } + + return super._swap( + fromToken, + toToken, + amount, + distribution, + flags + ); + } + + function _swapFromPoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + + uint256[] memory dist = new uint256[](distribution.length); + + ( + uint256 ethAmount, + uint256 exchangeTokenAmount + ) = IUniswapExchange(address(poolToken)).removeLiquidity( + amount, + 1, + 1, + now.add(1800) + ); + + if (!toToken.isETH()) { + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j]) & 0xFF; + } + + super._swap( + ETH_ADDRESS, + toToken, + ethAmount, + dist, + flags + ); + } + + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + + if (toToken != uniswapToken) { + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> 8) & 0xFF; + } + + super._swap( + uniswapToken, + toToken, + exchangeTokenAmount, + dist, + flags + ); + } + } + + function _swapToPoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + uint256[] memory dist = new uint256[](distribution.length); + + uint256 partAmountForEth = amount.div(2); + if (!fromToken.isETH()) { + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j]) & 0xFF; + } + + super._swap( + fromToken, + ETH_ADDRESS, + partAmountForEth, + dist, + flags + ); + } + + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + + uint256 partAmountForToken = amount.sub(partAmountForEth); + if (fromToken != uniswapToken) { + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> 8) & 0xFF; + } + + super._swap( + fromToken, + uniswapToken, + partAmountForToken, + dist, + flags + ); + + _infiniteApproveIfNeeded(uniswapToken, address(poolToken)); + } + + uint256 ethBalance = address(this).balance; + uint256 tokenBalance = uniswapToken.balanceOf(address(this)); + + (uint256 ethAmount, uint256 returnAmount) = getMaxPossibleFund( + poolToken, + uniswapToken, + tokenBalance, + ethBalance + ); + + IUniswapExchange(address(poolToken)).addLiquidity.value(ethAmount)( + returnAmount.mul(995).div(1000), // 0.5% slippage + uint256(-1), // todo: think about another value + now.add(1800) + ); + + // todo: do we need to check difference between balance before and balance after? + uniswapToken.universalTransfer(msg.sender, uniswapToken.balanceOf(address(this))); + ETH_ADDRESS.universalTransfer(msg.sender, address(this).balance); + } +} + +// File: contracts/OneSplitCurvePoolToken.sol + +pragma solidity ^0.5.0; + + + + +contract OneSplitCurvePoolTokenBase { + using SafeMath for uint256; + using UniversalERC20 for IERC20; + + IERC20 constant curveSusdToken = IERC20(0xC25a3A3b969415c80451098fa907EC722572917F); + IERC20 constant curveIearnToken = IERC20(0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8); + IERC20 constant curveCompoundToken = IERC20(0x845838DF265Dcd2c412A1Dc9e959c7d08537f8a2); + IERC20 constant curveUsdtToken = IERC20(0x9fC689CCaDa600B6DF723D9E47D84d76664a1F23); + IERC20 constant curveBinanceToken = IERC20(0x3B3Ac5386837Dc563660FB6a0937DFAa5924333B); + + ICurve constant curveSusd = ICurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); + ICurve constant curveIearn = ICurve(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); + ICurve constant curveCompound = ICurve(0xA2B47E3D5c44877cca798226B7B8118F9BFb7A56); + ICurve constant curveUsdt = ICurve(0x52EA46506B9CC5Ef470C5bf89f17Dc28bB35D85C); + ICurve constant curveBinance = ICurve(0x79a8C46DeA5aDa233ABaFFD40F3A0A2B1e5A4F27); + + struct CurveTokenInfo { + IERC20 token; + uint256 weightedReserveBalance; + } + + struct CurveInfo { + ICurve curve; + uint256 tokenCount; + } + + struct CurvePoolTokenDetails { + CurveTokenInfo[] tokens; + uint256 totalWeightedBalance; + } + + function _isPoolToken(IERC20 token) + internal + pure + returns (bool) + { + if ( + token == curveSusdToken || + token == curveIearnToken || + token == curveCompoundToken || + token == curveUsdtToken || + token == curveBinanceToken + ) { + return true; + } + return false; + } + + function _getCurve(IERC20 poolToken) + internal + pure + returns (CurveInfo memory curveInfo) + { + if (poolToken == curveSusdToken) { + curveInfo.curve = curveSusd; + curveInfo.tokenCount = 4; + return curveInfo; + } + + if (poolToken == curveIearnToken) { + curveInfo.curve = curveIearn; + curveInfo.tokenCount = 4; + return curveInfo; + } + + if (poolToken == curveCompoundToken) { + curveInfo.curve = curveCompound; + curveInfo.tokenCount = 2; + return curveInfo; + } + + if (poolToken == curveUsdtToken) { + curveInfo.curve = curveUsdt; + curveInfo.tokenCount = 3; + return curveInfo; + } + + if (poolToken == curveBinanceToken) { + curveInfo.curve = curveBinance; + curveInfo.tokenCount = 4; + return curveInfo; + } + + revert(); + } + + function _getCurveCalcTokenAmountSelector(uint256 tokenCount) + internal + pure + returns (bytes4) + { + return bytes4(keccak256(abi.encodePacked( + "calc_token_amount(uint256[", uint8(48 + tokenCount) ,"],bool)" + ))); + } + + function _getCurveRemoveLiquiditySelector(uint256 tokenCount) + internal + pure + returns (bytes4) + { + return bytes4(keccak256(abi.encodePacked( + "remove_liquidity(uint256,uint256[", uint8(48 + tokenCount) ,"])" + ))); + } + + function _getCurveAddLiquiditySelector(uint256 tokenCount) + internal + pure + returns (bytes4) + { + return bytes4(keccak256(abi.encodePacked( + "add_liquidity(uint256[", uint8(48 + tokenCount) ,"],uint256)" + ))); + } + + function _getPoolDetails(ICurve curve, uint256 tokenCount) + internal + view + returns(CurvePoolTokenDetails memory details) + { + details.tokens = new CurveTokenInfo[](tokenCount); + for (uint256 i = 0; i < tokenCount; i++) { + details.tokens[i].token = IERC20(curve.coins(int128(i))); + details.tokens[i].weightedReserveBalance = curve.balances(int128(i)) + .mul(1e18).div(10 ** details.tokens[i].token.universalDecimals()); + details.totalWeightedBalance = details.totalWeightedBalance.add( + details.tokens[i].weightedReserveBalance + ); + } + } +} + + +contract OneSplitCurvePoolTokenView is OneSplitViewWrapBase, OneSplitCurvePoolTokenBase { + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + public + view + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) + { + if (fromToken == toToken) { + return (amount, new uint256[](DEXES_COUNT)); + } + + + if (!flags.check(FLAG_DISABLE_CURVE_ZAP)) { + if (_isPoolToken(fromToken)) { + return _getExpectedReturnFromCurvePoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_CURVE_ZAP + ); + } + + if (_isPoolToken(toToken)) { + return _getExpectedReturnToCurvePoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_CURVE_ZAP + ); + } + } + + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + flags + ); + } + + function _getExpectedReturnFromCurvePoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + CurveInfo memory curveInfo = _getCurve(poolToken); + uint256 totalSupply = poolToken.totalSupply(); + for (uint i = 0; i < curveInfo.tokenCount; i++) { + IERC20 coin = IERC20(curveInfo.curve.coins(int128(i))); + + uint256 tokenAmountOut = curveInfo.curve.balances(int128(i)) + .mul(amount) + .div(totalSupply); + + if (coin == toToken) { + returnAmount = returnAmount.add(tokenAmountOut); + continue; + } + + (uint256 ret, uint256[] memory dist) = this.getExpectedReturn( + coin, + toToken, + tokenAmountOut, + parts, + flags + ); + + returnAmount = returnAmount.add(ret); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + return (returnAmount, distribution); + } + + function _getExpectedReturnToCurvePoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + CurveInfo memory curveInfo = _getCurve(poolToken); + CurvePoolTokenDetails memory details = _getPoolDetails( + curveInfo.curve, + curveInfo.tokenCount + ); + + bytes memory tokenAmounts; + for (uint i = 0; i < curveInfo.tokenCount; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].weightedReserveBalance) + .div(details.totalWeightedBalance); + + if (details.tokens[i].token == fromToken) { + tokenAmounts = abi.encodePacked(tokenAmounts, exchangeAmount); + continue; + } + + (uint256 tokenAmount, uint256[] memory dist) = this.getExpectedReturn( + fromToken, + details.tokens[i].token, + exchangeAmount, + parts, + flags + ); + + tokenAmounts = abi.encodePacked(tokenAmounts, tokenAmount); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + (bool success, bytes memory data) = address(curveInfo.curve).staticcall( + abi.encodePacked( + _getCurveCalcTokenAmountSelector(curveInfo.tokenCount), + tokenAmounts, + uint256(1) + ) + ); + + require(success, 'calc_token_amount failed'); + + return (abi.decode(data, (uint256)), distribution); + } +} + + +contract OneSplitCurvePoolToken is OneSplitBaseWrap, OneSplitCurvePoolTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) internal { + if (fromToken == toToken) { + return; + } + + if (!flags.check(FLAG_DISABLE_CURVE_ZAP)) { + if (_isPoolToken(fromToken)) { + return _swapFromCurvePoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_CURVE_ZAP + ); + } + + if (_isPoolToken(toToken)) { + return _swapToCurvePoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_CURVE_ZAP + ); + } + } + + return super._swap( + fromToken, + toToken, + amount, + distribution, + flags + ); + } + + function _swapFromCurvePoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + CurveInfo memory curveInfo = _getCurve(poolToken); + + bytes memory minAmountsOut; + for (uint i = 0; i < curveInfo.tokenCount; i++) { + minAmountsOut = abi.encodePacked(minAmountsOut, uint256(1)); + } + + (bool success,) = address(curveInfo.curve).call( + abi.encodePacked( + _getCurveRemoveLiquiditySelector(curveInfo.tokenCount), + amount, + minAmountsOut + ) + ); + + require(success, 'remove_liquidity failed'); + + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < curveInfo.tokenCount; i++) { + IERC20 coin = IERC20(curveInfo.curve.coins(int128(i))); + + if (coin == toToken) { + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + uint256 exchangeTokenAmount = coin.universalBalanceOf(address(this)); + + this.swap( + coin, + toToken, + exchangeTokenAmount, + 0, + dist, + flags + ); + } + } + + function _swapToCurvePoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + uint256[] memory dist = new uint256[](distribution.length); + + CurveInfo memory curveInfo = _getCurve(poolToken); + CurvePoolTokenDetails memory details = _getPoolDetails( + curveInfo.curve, + curveInfo.tokenCount + ); + + bytes memory tokenAmounts; + for (uint i = 0; i < curveInfo.tokenCount; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].weightedReserveBalance) + .div(details.totalWeightedBalance); + + _infiniteApproveIfNeeded(details.tokens[i].token, address(curveInfo.curve)); + + if (details.tokens[i].token == fromToken) { + tokenAmounts = abi.encodePacked(tokenAmounts, exchangeAmount); + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + this.swap( + fromToken, + details.tokens[i].token, + exchangeAmount, + 0, + dist, + flags + ); + + tokenAmounts = abi.encodePacked( + tokenAmounts, + details.tokens[i].token.universalBalanceOf(address(this)) + ); + } + + (bool success,) = address(curveInfo.curve).call( + abi.encodePacked( + _getCurveAddLiquiditySelector(curveInfo.tokenCount), + tokenAmounts, + uint256(0) + ) + ); + + require(success, 'add_liquidity failed'); + } +} + +// File: contracts/interface/ISmartTokenConverter.sol + +pragma solidity ^0.5.0; + + +interface ISmartTokenConverter { + + function version() external view returns (uint16); + + function connectors(address) external view returns (uint256, uint32, bool, bool, bool); + + function getReserveRatio(IERC20 token) external view returns (uint256); + + function connectorTokenCount() external view returns (uint256); + + function connectorTokens(uint256 i) external view returns (IERC20); + + function liquidate(uint256 _amount) external; + + function fund(uint256 _amount) external; + + function convert2(IERC20 _fromToken, IERC20 _toToken, uint256 _amount, uint256 _minReturn, address _affiliateAccount, uint256 _affiliateFee) external returns (uint256); + + function convert(IERC20 _fromToken, IERC20 _toToken, uint256 _amount, uint256 _minReturn) external returns (uint256); + +} + +// File: contracts/interface/ISmartToken.sol + +pragma solidity ^0.5.0; + + + + +interface ISmartToken { + function owner() external view returns (ISmartTokenConverter); +} + +// File: contracts/interface/ISmartTokenRegistry.sol + +pragma solidity ^0.5.0; + + + +interface ISmartTokenRegistry { + function isSmartToken(IERC20 token) external view returns (bool); +} + +// File: contracts/interface/ISmartTokenFormula.sol + +pragma solidity ^0.5.0; + + + +interface ISmartTokenFormula { + function _calculateLiquidateReturn( + uint256 supply, + uint256 reserveBalance, + uint32 totalRatio, + uint256 amount + ) external view returns (uint256); + + function _calculatePurchaseReturn( + uint256 supply, + uint256 reserveBalance, + uint32 totalRatio, + uint256 amount + ) external view returns (uint256); +} + +// File: contracts/OneSplitSmartToken.sol + +pragma solidity ^0.5.0; + + + + + + + +contract OneSplitSmartTokenBase { + using SafeMath for uint256; + + ISmartTokenRegistry smartTokenRegistry = ISmartTokenRegistry(0xf6E2D7F616B67E46D708e4410746E9AAb3a4C518); + ISmartTokenFormula smartTokenFormula = ISmartTokenFormula(0x524619EB9b4cdFFa7DA13029b33f24635478AFc0); + IERC20 bntToken = IERC20(0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C); + IERC20 usdbToken = IERC20(0x309627af60F0926daa6041B8279484312f2bf060); + + IERC20 public susd = IERC20(0x57Ab1ec28D129707052df4dF418D58a2D46d5f51); + IERC20 public acientSUSD = IERC20(0x57Ab1E02fEE23774580C119740129eAC7081e9D3); + + struct TokenWithRatio { + IERC20 token; + uint256 ratio; + } + + struct SmartTokenDetails { + TokenWithRatio[] tokens; + address converter; + uint256 totalRatio; + } + + function _getSmartTokenDetails(ISmartToken smartToken) + internal + view + returns(SmartTokenDetails memory details) + { + ISmartTokenConverter converter = smartToken.owner(); + details.converter = address(converter); + details.tokens = new TokenWithRatio[](converter.connectorTokenCount()); + + for (uint256 i = 0; i < details.tokens.length; i++) { + details.tokens[i].token = converter.connectorTokens(i); + details.tokens[i].ratio = _getReserveRatio(converter, details.tokens[i].token); + details.totalRatio = details.totalRatio.add(details.tokens[i].ratio); + } + } + + function _getReserveRatio( + ISmartTokenConverter converter, + IERC20 token + ) + internal + view + returns (uint256) + { + (bool success, bytes memory data) = address(converter).staticcall.gas(10000)( + abi.encodeWithSelector( + converter.getReserveRatio.selector, + token + ) + ); + + if (success) { + return abi.decode(data, (uint256)); + } + + (, uint32 ratio, , ,) = converter.connectors(address(token)); + + return uint256(ratio); + } + + function _canonicalSUSD(IERC20 token) internal view returns(IERC20) { + return token == acientSUSD ? susd : token; + } +} + + +contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase { + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + public + view + returns( + uint256, + uint256[] memory + ) + { + if (fromToken == toToken) { + return (amount, new uint256[](DEXES_COUNT)); + } + + if (!flags.check(FLAG_DISABLE_SMART_TOKEN)) { + bool isSmartTokenFrom = smartTokenRegistry.isSmartToken(fromToken); + bool isSmartTokenTo = smartTokenRegistry.isSmartToken(toToken); + + if (isSmartTokenFrom && isSmartTokenTo) { + ( + uint256 returnBntAmount, + uint256[] memory smartTokenFromDistribution + ) = _getExpectedReturnFromSmartToken( + fromToken, + bntToken, + amount, + parts, + 0 + ); + + ( + uint256 returnSmartTokenToAmount, + uint256[] memory smartTokenToDistribution + ) = _getExpectedReturnToSmartToken( + bntToken, + toToken, + returnBntAmount, + parts, + 0 + ); + + for (uint i = 0; i < smartTokenToDistribution.length; i++) { + smartTokenFromDistribution[i] |= smartTokenToDistribution[i] << 128; + } + + return (returnSmartTokenToAmount, smartTokenFromDistribution); + } + + if (isSmartTokenFrom) { + return _getExpectedReturnFromSmartToken( + fromToken, + toToken, + amount, + parts, + 0 + ); + } + + if (isSmartTokenTo) { + return _getExpectedReturnToSmartToken( + fromToken, + toToken, + amount, + parts, + 0 + ); } } + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + flags + ); } - function convert( - ISmartTokenConverter converter, - IERC20 _fromToken, - IERC20 _toToken, - uint256 _amount + function _getExpectedReturnFromSmartToken( + IERC20 smartToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags ) private - returns (uint256) + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); + + for (uint i = 0; i < details.tokens.length; i++) { + uint256 srcAmount = smartTokenFormula._calculateLiquidateReturn( + smartToken.totalSupply(), + _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), + uint32(details.totalRatio), + amount + ); + + if (details.tokens[i].token == toToken) { + returnAmount = returnAmount.add(srcAmount); + continue; + } + + (uint256 ret, uint256[] memory dist) = getExpectedReturn( + _canonicalSUSD(details.tokens[i].token), + toToken, + srcAmount, + parts, + flags + ); + + returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + return (returnAmount, distribution); + } + + function _getExpectedReturnToSmartToken( + IERC20 fromToken, + IERC20 smartToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns( + uint256 minFundAmount, + uint256[] memory distribution + ) { - // todo: think about minReturn, affiliateAccount, affiliateFee - if (converter.version() >= 16) { - return converter.convert2(_fromToken, _toToken, _amount, 0, address(0), 0); + distribution = new uint256[](DEXES_COUNT); + minFundAmount = uint256(-1); + + SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); + + uint256[] memory tokenAmounts = new uint256[](details.tokens.length); + uint256[] memory dist; + uint256[] memory fundAmounts = new uint256[](details.tokens.length); + + for (uint i = 0; i < details.tokens.length; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].ratio) + .div(details.totalRatio); + + if (details.tokens[i].token != fromToken) { + (tokenAmounts[i], dist) = getExpectedReturn( + fromToken, + _canonicalSUSD(details.tokens[i].token), + exchangeAmount, + parts, + flags + ); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } else { + tokenAmounts[i] = exchangeAmount; + } + + fundAmounts[i] = smartTokenFormula._calculatePurchaseReturn( + smartToken.totalSupply(), + _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), + uint32(details.totalRatio), + tokenAmounts[i] + ); + + if (fundAmounts[i] < minFundAmount) { + minFundAmount = fundAmounts[i]; + } + } + + uint256 _minFundAmount = minFundAmount; + IERC20 _smartToken = smartToken; + + // Swap leftovers for SmartToken + for (uint i = 0; i < details.tokens.length; i++) { + if (_minFundAmount == fundAmounts[i]) { + continue; + } + + uint256 leftover = tokenAmounts[i].sub( + smartTokenFormula._calculateLiquidateReturn( + _smartToken.totalSupply().add(_minFundAmount), + _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter).add(tokenAmounts[i]), + uint32(details.totalRatio), + _minFundAmount + ) + ); + + uint256 tokenRet = _calculateBancorReturn( + _canonicalSUSD(details.tokens[i].token), + _smartToken, + leftover, + flags + ); + + minFundAmount = minFundAmount.add(tokenRet); + } + + return (minFundAmount, distribution); + } +} + + +contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) internal { + + if (fromToken == toToken) { + return; + } + + if (!flags.check(FLAG_DISABLE_SMART_TOKEN)) { + + bool isSmartTokenFrom = smartTokenRegistry.isSmartToken(fromToken); + bool isSmartTokenTo = smartTokenRegistry.isSmartToken(toToken); + + if (isSmartTokenFrom && isSmartTokenTo) { + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] & ((1 << 128) - 1); + } + + uint256 bntBalanceBefore = bntToken.balanceOf(address(this)); + + _swapFromSmartToken( + fromToken, + bntToken, + amount, + dist, + 0 + ); + + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] >> 128; + } + + uint256 bntBalanceAfter = bntToken.balanceOf(address(this)); + + return _swapToSmartToken( + bntToken, + toToken, + bntBalanceAfter.sub(bntBalanceBefore), + dist, + 0 + ); + } + + if (isSmartTokenFrom) { + return _swapFromSmartToken( + fromToken, + toToken, + amount, + distribution, + 0 + ); + } + + if (isSmartTokenTo) { + return _swapToSmartToken( + fromToken, + toToken, + amount, + distribution, + 0 + ); + } } - return converter.convert(_fromToken, _toToken, _amount, 0); + return super._swap( + fromToken, + toToken, + amount, + distribution, + flags + ); + } + + function _swapFromSmartToken( + IERC20 smartToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); + + ISmartTokenConverter(details.converter).liquidate(amount); + + uint256[] memory dist = new uint256[](distribution.length); + + for (uint i = 0; i < details.tokens.length; i++) { + if (details.tokens[i].token == toToken) { + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + this.swap( + _canonicalSUSD(details.tokens[i].token), + toToken, + _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)), + 0, + dist, + flags + ); + } } + function _swapToSmartToken( + IERC20 fromToken, + IERC20 smartToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + + uint256[] memory dist = new uint256[](distribution.length); + uint256 minFundAmount = uint256(-1); + + SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); + + uint256 curFundAmount; + for (uint i = 0; i < details.tokens.length; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].ratio) + .div(details.totalRatio); + + if (details.tokens[i].token != fromToken) { + + uint256 tokenBalanceBefore = _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)); + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + super._swap( + fromToken, + _canonicalSUSD(details.tokens[i].token), + exchangeAmount, + dist, + flags + ); + + uint256 tokenBalanceAfter = _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)); + + curFundAmount = smartTokenFormula._calculatePurchaseReturn( + smartToken.totalSupply(), + _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), + uint32(details.totalRatio), + tokenBalanceAfter.sub(tokenBalanceBefore) + ); + } else { + curFundAmount = smartTokenFormula._calculatePurchaseReturn( + smartToken.totalSupply(), + _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), + uint32(details.totalRatio), + exchangeAmount + ); + } + + if (curFundAmount < minFundAmount) { + minFundAmount = curFundAmount; + } + + _infiniteApproveIfNeeded(_canonicalSUSD(details.tokens[i].token), details.converter); + } + + ISmartTokenConverter(details.converter).fund(minFundAmount); + + // Swap leftovers for SmartToken + for (uint i = 0; i < details.tokens.length; i++) { + IERC20 reserveToken = _canonicalSUSD(details.tokens[i].token); + + uint256 leftover = _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)); + + uint256 ret = this._swapOnBancorSafe( + reserveToken, + smartToken, + leftover + ); + + if (ret == 0) { + reserveToken.universalTransfer(msg.sender, leftover); + } + } + } } // File: contracts/OneSplit.sol pragma solidity ^0.5.0; -pragma experimental ABIEncoderV2; + + + + + + + + + + + + + + + + contract OneSplitViewWrap is OneSplitViewWrapBase, - OneSplitMultiPathView, - OneSplitChaiView, - OneSplitBdaiView, - OneSplitAaveView, - OneSplitFulcrumView, + //OneSplitMultiPathView, + //OneSplitChaiView, + //OneSplitBdaiView, + //OneSplitAaveView, + //OneSplitFulcrumView, OneSplitCompoundView, OneSplitIearnView, - OneSplitIdleView, - OneSplitWethView + //OneSplitIdleView, + OneSplitWethView, + OneSplitBalancerPoolTokenView, + OneSplitUniswapPoolTokenView, + OneSplitCurvePoolTokenView, OneSplitSmartTokenView { IOneSplitView public oneSplitView; @@ -4364,20 +5911,37 @@ contract OneSplitViewWrap is flags ); } + + function _calculateBancorReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 flags + ) public view returns(uint256) { + return oneSplitView._calculateBancorReturn( + fromToken, + toToken, + amount, + flags + ); + } } contract OneSplitWrap is OneSplitBaseWrap, - OneSplitMultiPath, - OneSplitChai, - OneSplitBdai, - OneSplitAave, - OneSplitFulcrum, + //OneSplitMultiPath, + //OneSplitChai, + //OneSplitBdai, + //OneSplitAave, + //OneSplitFulcrum, OneSplitCompound, OneSplitIearn, - OneSplitIdle, - OneSplitWeth + //OneSplitIdle, + OneSplitWeth, + //OneSplitBalancerPoolToken, + OneSplitUniswapPoolToken, + OneSplitCurvePoolToken, OneSplitSmartToken { IOneSplitView public oneSplitView; @@ -4424,14 +5988,19 @@ contract OneSplitWrap is uint256[] memory distribution, // [Uniswap, Kyber, Bancor, Oasis] uint256 flags // 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI ) public payable { - fromToken.universalTransferFrom(msg.sender, address(this), amount); + if (msg.sender != address(this)) { + fromToken.universalTransferFrom(msg.sender, address(this), amount); + } _swap(fromToken, toToken, amount, distribution, flags); uint256 returnAmount = toToken.universalBalanceOf(address(this)); require(returnAmount >= minReturn, "OneSplit: actual return amount is less than minReturn"); - toToken.universalTransfer(msg.sender, returnAmount); - fromToken.universalTransfer(msg.sender, fromToken.universalBalanceOf(address(this))); + + if (msg.sender != address(this)) { + toToken.universalTransfer(msg.sender, returnAmount); + fromToken.universalTransfer(msg.sender, fromToken.universalBalanceOf(address(this))); + } } function _swapFloor( @@ -4459,4 +6028,26 @@ contract OneSplitWrap is case 0 { revert(add(data, 32), returndatasize) } } } + + function _swapOnBancorSafe( + IERC20 fromToken, + IERC20 toToken, + uint256 amount + ) external returns(uint256) { + (bool success, bytes memory data) = address(oneSplit).delegatecall( + abi.encodeWithSelector( + this._swapOnBancorSafe.selector, + fromToken, + toToken, + amount + ) + ); + + assembly { + switch success + // delegatecall returns 0 on error. + case 0 { revert(add(data, 32), returndatasize) } + case 1 { return(add(data, 32), returndatasize) } + } + } } diff --git a/contracts/IOneSplit.sol b/contracts/IOneSplit.sol index c20050a..a45c22f 100644 --- a/contracts/IOneSplit.sol +++ b/contracts/IOneSplit.sol @@ -51,30 +51,7 @@ contract IOneSplitConsts { uint256 public constant FLAG_DISABLE_IDLE = 0x800000; uint256 public constant FLAG_DISABLE_UNISWAP_POOL_TOKEN = 0x1000000; uint256 public constant FLAG_DISABLE_BALANCER_POOL_TOKEN = 0x2000000; - uint256 public constant FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN = 0x4000000; - - uint256 public constant FLAG_DISABLE_ALL_SOURCES = - FLAG_DISABLE_UNISWAP | - FLAG_DISABLE_KYBER | - FLAG_DISABLE_BANCOR | - FLAG_DISABLE_OASIS | - FLAG_DISABLE_CURVE_COMPOUND | - FLAG_DISABLE_CURVE_USDT | - FLAG_DISABLE_CURVE_Y | - FLAG_DISABLE_CURVE_BINANCE | - FLAG_DISABLE_CURVE_SYNTHETIX; - - uint256 public constant FLAG_DISABLE_ALL_WRAPPERS = - FLAG_DISABLE_COMPOUND | - FLAG_DISABLE_FULCRUM | - FLAG_DISABLE_CHAI | - FLAG_DISABLE_AAVE | - FLAG_DISABLE_SMART_TOKEN | - FLAG_DISABLE_BDAI | - FLAG_DISABLE_IEARN | - FLAG_DISABLE_WETH; - - uint256 public constant FLAG_DISABLE_ALL = FLAG_DISABLE_ALL_SOURCES | FLAG_DISABLE_ALL_WRAPPERS; + uint256 public constant FLAG_DISABLE_CURVE_ZAP = 0x4000000; } contract IOneSplit is IOneSplitConsts { diff --git a/contracts/OneSplit.sol b/contracts/OneSplit.sol index 28355ce..1bc1c39 100644 --- a/contracts/OneSplit.sol +++ b/contracts/OneSplit.sol @@ -13,7 +13,7 @@ import "./OneSplitAave.sol"; import "./OneSplitWeth.sol"; import "./OneSplitBalancerPoolToken.sol"; import "./OneSplitUniswapPoolToken.sol"; -import "./OneSplitCurveSusdPoolToken.sol"; +import "./OneSplitCurvePoolToken.sol"; import "./OneSplitSmartToken.sol"; @@ -30,7 +30,7 @@ contract OneSplitViewWrap is OneSplitWethView, OneSplitBalancerPoolTokenView, OneSplitUniswapPoolTokenView, - OneSplitCurveSusdPoolTokenView, + OneSplitCurvePoolTokenView, OneSplitSmartTokenView { IOneSplitView public oneSplitView; @@ -118,7 +118,7 @@ contract OneSplitWrap is OneSplitWeth, OneSplitBalancerPoolToken, OneSplitUniswapPoolToken, - OneSplitCurveSusdPoolToken, + OneSplitCurvePoolToken, OneSplitSmartToken { IOneSplitView public oneSplitView; diff --git a/contracts/OneSplitCurvePoolToken.sol b/contracts/OneSplitCurvePoolToken.sol new file mode 100644 index 0000000..6a0abc6 --- /dev/null +++ b/contracts/OneSplitCurvePoolToken.sol @@ -0,0 +1,452 @@ +pragma solidity ^0.5.0; + +import "./OneSplitBase.sol"; +import "./interface/ICurve.sol"; + + +contract OneSplitCurvePoolTokenBase { + using SafeMath for uint256; + using UniversalERC20 for IERC20; + + IERC20 constant curveSusdToken = IERC20(0xC25a3A3b969415c80451098fa907EC722572917F); + IERC20 constant curveIearnToken = IERC20(0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8); + IERC20 constant curveCompoundToken = IERC20(0x845838DF265Dcd2c412A1Dc9e959c7d08537f8a2); + IERC20 constant curveUsdtToken = IERC20(0x9fC689CCaDa600B6DF723D9E47D84d76664a1F23); + IERC20 constant curveBinanceToken = IERC20(0x3B3Ac5386837Dc563660FB6a0937DFAa5924333B); + + ICurve constant curveSusd = ICurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); + ICurve constant curveIearn = ICurve(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); + ICurve constant curveCompound = ICurve(0xA2B47E3D5c44877cca798226B7B8118F9BFb7A56); + ICurve constant curveUsdt = ICurve(0x52EA46506B9CC5Ef470C5bf89f17Dc28bB35D85C); + ICurve constant curveBinance = ICurve(0x79a8C46DeA5aDa233ABaFFD40F3A0A2B1e5A4F27); + + struct CurveTokenInfo { + IERC20 token; + uint256 weightedReserveBalance; + } + + struct CurveInfo { + ICurve curve; + uint256 tokenCount; + } + + struct CurvePoolTokenDetails { + CurveTokenInfo[] tokens; + uint256 totalWeightedBalance; + } + + function _isPoolToken(IERC20 token) + internal + pure + returns (bool) + { + if ( + token == curveSusdToken || + token == curveIearnToken || + token == curveCompoundToken || + token == curveUsdtToken || + token == curveBinanceToken + ) { + return true; + } + return false; + } + + function _getCurve(IERC20 poolToken) + internal + pure + returns (CurveInfo memory curveInfo) + { + if (poolToken == curveSusdToken) { + curveInfo.curve = curveSusd; + curveInfo.tokenCount = 4; + return curveInfo; + } + + if (poolToken == curveIearnToken) { + curveInfo.curve = curveIearn; + curveInfo.tokenCount = 4; + return curveInfo; + } + + if (poolToken == curveCompoundToken) { + curveInfo.curve = curveCompound; + curveInfo.tokenCount = 2; + return curveInfo; + } + + if (poolToken == curveUsdtToken) { + curveInfo.curve = curveUsdt; + curveInfo.tokenCount = 3; + return curveInfo; + } + + if (poolToken == curveBinanceToken) { + curveInfo.curve = curveBinance; + curveInfo.tokenCount = 4; + return curveInfo; + } + + revert(); + } + + function _getCurveCalcTokenAmountSelector(uint256 tokenCount) + internal + pure + returns (bytes4) + { + return bytes4(keccak256(abi.encodePacked( + "calc_token_amount(uint256[", uint8(48 + tokenCount) ,"],bool)" + ))); + } + + function _getCurveRemoveLiquiditySelector(uint256 tokenCount) + internal + pure + returns (bytes4) + { + return bytes4(keccak256(abi.encodePacked( + "remove_liquidity(uint256,uint256[", uint8(48 + tokenCount) ,"])" + ))); + } + + function _getCurveAddLiquiditySelector(uint256 tokenCount) + internal + pure + returns (bytes4) + { + return bytes4(keccak256(abi.encodePacked( + "add_liquidity(uint256[", uint8(48 + tokenCount) ,"],uint256)" + ))); + } + + function _getPoolDetails(ICurve curve, uint256 tokenCount) + internal + view + returns(CurvePoolTokenDetails memory details) + { + details.tokens = new CurveTokenInfo[](tokenCount); + for (uint256 i = 0; i < tokenCount; i++) { + details.tokens[i].token = IERC20(curve.coins(int128(i))); + details.tokens[i].weightedReserveBalance = curve.balances(int128(i)) + .mul(1e18).div(10 ** details.tokens[i].token.universalDecimals()); + details.totalWeightedBalance = details.totalWeightedBalance.add( + details.tokens[i].weightedReserveBalance + ); + } + } +} + + +contract OneSplitCurvePoolTokenView is OneSplitViewWrapBase, OneSplitCurvePoolTokenBase { + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + public + view + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) + { + if (fromToken == toToken) { + return (amount, new uint256[](DEXES_COUNT)); + } + + + if (!flags.check(FLAG_DISABLE_CURVE_ZAP)) { + if (_isPoolToken(fromToken)) { + return _getExpectedReturnFromCurvePoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_CURVE_ZAP + ); + } + + if (_isPoolToken(toToken)) { + return _getExpectedReturnToCurvePoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_CURVE_ZAP + ); + } + } + + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + flags + ); + } + + function _getExpectedReturnFromCurvePoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + CurveInfo memory curveInfo = _getCurve(poolToken); + uint256 totalSupply = poolToken.totalSupply(); + for (uint i = 0; i < curveInfo.tokenCount; i++) { + IERC20 coin = IERC20(curveInfo.curve.coins(int128(i))); + + uint256 tokenAmountOut = curveInfo.curve.balances(int128(i)) + .mul(amount) + .div(totalSupply); + + if (coin == toToken) { + returnAmount = returnAmount.add(tokenAmountOut); + continue; + } + + (uint256 ret, uint256[] memory dist) = this.getExpectedReturn( + coin, + toToken, + tokenAmountOut, + parts, + flags + ); + + returnAmount = returnAmount.add(ret); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + return (returnAmount, distribution); + } + + function _getExpectedReturnToCurvePoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + CurveInfo memory curveInfo = _getCurve(poolToken); + CurvePoolTokenDetails memory details = _getPoolDetails( + curveInfo.curve, + curveInfo.tokenCount + ); + + bytes memory tokenAmounts; + for (uint i = 0; i < curveInfo.tokenCount; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].weightedReserveBalance) + .div(details.totalWeightedBalance); + + if (details.tokens[i].token == fromToken) { + tokenAmounts = abi.encodePacked(tokenAmounts, exchangeAmount); + continue; + } + + (uint256 tokenAmount, uint256[] memory dist) = this.getExpectedReturn( + fromToken, + details.tokens[i].token, + exchangeAmount, + parts, + flags + ); + + tokenAmounts = abi.encodePacked(tokenAmounts, tokenAmount); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + (bool success, bytes memory data) = address(curveInfo.curve).staticcall( + abi.encodePacked( + _getCurveCalcTokenAmountSelector(curveInfo.tokenCount), + tokenAmounts, + uint256(1) + ) + ); + + require(success, 'calc_token_amount failed'); + + return (abi.decode(data, (uint256)), distribution); + } +} + + +contract OneSplitCurvePoolToken is OneSplitBaseWrap, OneSplitCurvePoolTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) internal { + if (fromToken == toToken) { + return; + } + + if (!flags.check(FLAG_DISABLE_CURVE_ZAP)) { + if (_isPoolToken(fromToken)) { + return _swapFromCurvePoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_CURVE_ZAP + ); + } + + if (_isPoolToken(toToken)) { + return _swapToCurvePoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_CURVE_ZAP + ); + } + } + + return super._swap( + fromToken, + toToken, + amount, + distribution, + flags + ); + } + + function _swapFromCurvePoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + CurveInfo memory curveInfo = _getCurve(poolToken); + + bytes memory minAmountsOut; + for (uint i = 0; i < curveInfo.tokenCount; i++) { + minAmountsOut = abi.encodePacked(minAmountsOut, uint256(1)); + } + + (bool success,) = address(curveInfo.curve).call( + abi.encodePacked( + _getCurveRemoveLiquiditySelector(curveInfo.tokenCount), + amount, + minAmountsOut + ) + ); + + require(success, 'remove_liquidity failed'); + + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < curveInfo.tokenCount; i++) { + IERC20 coin = IERC20(curveInfo.curve.coins(int128(i))); + + if (coin == toToken) { + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + uint256 exchangeTokenAmount = coin.universalBalanceOf(address(this)); + + this.swap( + coin, + toToken, + exchangeTokenAmount, + 0, + dist, + flags + ); + } + } + + function _swapToCurvePoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + uint256[] memory dist = new uint256[](distribution.length); + + CurveInfo memory curveInfo = _getCurve(poolToken); + CurvePoolTokenDetails memory details = _getPoolDetails( + curveInfo.curve, + curveInfo.tokenCount + ); + + bytes memory tokenAmounts; + for (uint i = 0; i < curveInfo.tokenCount; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].weightedReserveBalance) + .div(details.totalWeightedBalance); + + _infiniteApproveIfNeeded(details.tokens[i].token, address(curveInfo.curve)); + + if (details.tokens[i].token == fromToken) { + tokenAmounts = abi.encodePacked(tokenAmounts, exchangeAmount); + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + this.swap( + fromToken, + details.tokens[i].token, + exchangeAmount, + 0, + dist, + flags + ); + + tokenAmounts = abi.encodePacked( + tokenAmounts, + details.tokens[i].token.universalBalanceOf(address(this)) + ); + } + + (bool success,) = address(curveInfo.curve).call( + abi.encodePacked( + _getCurveAddLiquiditySelector(curveInfo.tokenCount), + tokenAmounts, + uint256(0) + ) + ); + + require(success, 'add_liquidity failed'); + } +} diff --git a/contracts/OneSplitCurveSusdPoolToken.sol b/contracts/OneSplitCurveSusdPoolToken.sol deleted file mode 100644 index a2fd7bb..0000000 --- a/contracts/OneSplitCurveSusdPoolToken.sol +++ /dev/null @@ -1,319 +0,0 @@ -pragma solidity ^0.5.0; - -import "./OneSplitBase.sol"; -import "./interface/ICurve.sol"; - - -contract OneSplitCurveSusdPoolTokenBase { - using SafeMath for uint256; - using UniversalERC20 for IERC20; - - IERC20 constant curveSusdToken = IERC20(0xC25a3A3b969415c80451098fa907EC722572917F); - ICurve constant curve = ICurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); - - struct CurveSusdTokenInfo { - IERC20 token; - uint256 weightedReserveBalance; - } - - struct CurveSusdPoolTokenDetails { - CurveSusdTokenInfo[] tokens; - uint256 totalWeightedBalance; - } - - function _getPoolDetails() - internal - view - returns(CurveSusdPoolTokenDetails memory details) - { - details.tokens = new CurveSusdTokenInfo[](4); - for (uint256 i = 0; i < 4; i++) { - details.tokens[i].token = IERC20(curve.coins(int128(i))); - details.tokens[i].weightedReserveBalance = curve.balances(int128(i)) - .mul(1e18).div(10 ** details.tokens[i].token.universalDecimals()); - details.totalWeightedBalance = details.totalWeightedBalance.add( - details.tokens[i].weightedReserveBalance - ); - } - } -} - - -contract OneSplitCurveSusdPoolTokenView is OneSplitViewWrapBase, OneSplitCurveSusdPoolTokenBase { - function getExpectedReturn( - IERC20 fromToken, - IERC20 toToken, - uint256 amount, - uint256 parts, - uint256 flags - ) - public - view - returns ( - uint256 returnAmount, - uint256[] memory distribution - ) - { - if (fromToken == toToken) { - return (amount, new uint256[](DEXES_COUNT)); - } - - - if (!flags.check(FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN)) { - if (fromToken == curveSusdToken) { - return _getExpectedReturnFromCurveSusdPoolToken( - fromToken, - toToken, - amount, - parts, - FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN - ); - } - - if (toToken == curveSusdToken) { - return _getExpectedReturnToCurveSusdPoolToken( - fromToken, - toToken, - amount, - parts, - FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN - ); - } - } - - return super.getExpectedReturn( - fromToken, - toToken, - amount, - parts, - flags - ); - } - - function _getExpectedReturnFromCurveSusdPoolToken( - IERC20 poolToken, - IERC20 toToken, - uint256 amount, - uint256 parts, - uint256 flags - ) - private - view - returns ( - uint256 returnAmount, - uint256[] memory distribution - ) - { - distribution = new uint256[](DEXES_COUNT); - - uint256 totalSupply = poolToken.totalSupply(); - for (uint i = 0; i < 4; i++) { - IERC20 coin = IERC20(curve.coins(int128(i))); - - uint256 tokenAmountOut = curve.balances(int128(i)) - .mul(amount) - .div(totalSupply); - - if (coin == toToken) { - returnAmount = returnAmount.add(tokenAmountOut); - continue; - } - - (uint256 ret, uint256[] memory dist) = this.getExpectedReturn( - coin, - toToken, - tokenAmountOut, - parts, - flags - ); - - returnAmount = returnAmount.add(ret); - - for (uint j = 0; j < distribution.length; j++) { - distribution[j] |= dist[j] << (i * 8); - } - } - - return (returnAmount, distribution); - } - - function _getExpectedReturnToCurveSusdPoolToken( - IERC20 fromToken, - IERC20, // poolToken - uint256 amount, - uint256 parts, - uint256 flags - ) - private - view - returns ( - uint256 returnAmount, - uint256[] memory distribution - ) - { - distribution = new uint256[](DEXES_COUNT); - - CurveSusdPoolTokenDetails memory details = _getPoolDetails(); - - uint256[4] memory tokenAmounts; - uint256[] memory dist; - for (uint i = 0; i < 4; i++) { - uint256 exchangeAmount = amount - .mul(details.tokens[i].weightedReserveBalance) - .div(details.totalWeightedBalance); - - if (details.tokens[i].token == fromToken) { - tokenAmounts[i] = exchangeAmount; - continue; - } - - (tokenAmounts[i], dist) = this.getExpectedReturn( - fromToken, - details.tokens[i].token, - exchangeAmount, - parts, - flags - ); - - for (uint j = 0; j < distribution.length; j++) { - distribution[j] |= dist[j] << (i * 8); - } - } - - returnAmount = curve.calc_token_amount(tokenAmounts, true); - - return (returnAmount, distribution); - } -} - - -contract OneSplitCurveSusdPoolToken is OneSplitBaseWrap, OneSplitCurveSusdPoolTokenBase { - function _swap( - IERC20 fromToken, - IERC20 toToken, - uint256 amount, - uint256[] memory distribution, - uint256 flags - ) internal { - if (fromToken == toToken) { - return; - } - - if (!flags.check(FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN)) { - if (fromToken == curveSusdToken) { - return _swapFromCurveSusdPoolToken( - fromToken, - toToken, - amount, - distribution, - FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN - ); - } - - if (toToken == curveSusdToken) { - return _swapToCurveSusdPoolToken( - fromToken, - toToken, - amount, - distribution, - FLAG_DISABLE_CURVE_SUSD_POOL_TOKEN - ); - } - } - - return super._swap( - fromToken, - toToken, - amount, - distribution, - flags - ); - } - - function _swapFromCurveSusdPoolToken( - IERC20 poolToken, - IERC20 toToken, - uint256 amount, - uint256[] memory distribution, - uint256 flags - ) private { - - uint256 totalSupply = poolToken.totalSupply(); - uint256[4] memory minAmountsOut; - for (uint i = 0; i < 4; i++) { - minAmountsOut[i] = curve.balances(int128(i)) - .mul(amount) - .div(totalSupply) - .mul(995).div(1000); // 0.5% slippage; - } - - curve.remove_liquidity(amount, minAmountsOut); - - uint256[] memory dist = new uint256[](distribution.length); - for (uint i = 0; i < 4; i++) { - IERC20 coin = IERC20(curve.coins(int128(i))); - - if (coin == toToken) { - continue; - } - - for (uint j = 0; j < distribution.length; j++) { - dist[j] = (distribution[j] >> (i * 8)) & 0xFF; - } - - uint256 exchangeTokenAmount = coin.universalBalanceOf(address(this)); - - this.swap( - coin, - toToken, - exchangeTokenAmount, - 0, - dist, - flags - ); - } - } - - function _swapToCurveSusdPoolToken( - IERC20 fromToken, - IERC20, // poolToken, - uint256 amount, - uint256[] memory distribution, - uint256 flags - ) private { - uint256[] memory dist = new uint256[](distribution.length); - - CurveSusdPoolTokenDetails memory details = _getPoolDetails(); - - uint256[4] memory tokenAmounts; - for (uint i = 0; i < 4; i++) { - uint256 exchangeAmount = amount - .mul(details.tokens[i].weightedReserveBalance) - .div(details.totalWeightedBalance); - - _infiniteApproveIfNeeded(details.tokens[i].token, address(curve)); - - if (details.tokens[i].token == fromToken) { - tokenAmounts[i] = exchangeAmount; - continue; - } - - for (uint j = 0; j < distribution.length; j++) { - dist[j] = (distribution[j] >> (i * 8)) & 0xFF; - } - - this.swap( - fromToken, - details.tokens[i].token, - exchangeAmount, - 0, - dist, - flags - ); - - tokenAmounts[i] = details.tokens[i].token.universalBalanceOf(address(this)); - } - - curve.add_liquidity(tokenAmounts, 0); - } -} diff --git a/contracts/interface/ICurve.sol b/contracts/interface/ICurve.sol index f3deb7e..64977bb 100644 --- a/contracts/interface/ICurve.sol +++ b/contracts/interface/ICurve.sol @@ -13,10 +13,4 @@ interface ICurve { function coins(int128 arg0) external view returns (address); function balances(int128 arg0) external view returns (uint256); - - function add_liquidity(uint256[4] calldata amounts, uint256 min_mint_amount) external; - - function remove_liquidity(uint256 _amount, uint256[4] calldata min_amounts) external; - - function calc_token_amount(uint256[4] calldata amounts, bool deposit) external view returns (uint256); } From 2b991238bc689b57e5a8f325d63d1b6d4f876d2c Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 4 May 2020 03:08:58 +0300 Subject: [PATCH 52/60] tried to fix smart token --- OneSplit.full.bin | 2 +- OneSplit.full.sol | 26 +++++++++++++------------- contracts/OneSplitSmartToken.sol | 24 ++++++++++++------------ 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/OneSplit.full.bin b/OneSplit.full.bin index 834cefb..0d456a3 100644 --- a/OneSplit.full.bin +++ b/OneSplit.full.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b50604051613ff4380380613ff48339818101604052602081101561003357600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055613f8f806100656000396000f3fe6080604052600436106103815760003560e01c806375b5be2d116101d1578063c925777511610102578063d70a2d1f116100a0578063f4b9fa751161006f578063f4b9fa7514610976578063f56e281f1461098b578063f69e2046146109a0578063fbe4ed95146109b557610381565b8063d70a2d1f1461086d578063d77366a414610882578063dc1536b214610897578063e2a7515e146108ac57610381565b8063cc26e9fc116100dc578063cc26e9fc14610819578063cede5f6a1461082e578063d1aee5e314610843578063d393c3e91461085857610381565b8063c9257775146107da578063c989b667146107ef578063c9b42c671461080457610381565b8063a1b4d0111161016f578063b3bc784411610149578063b3bc784414610786578063b69d04561461079b578063c762a46c146107b0578063c77b9de6146107c557610381565b8063a1b4d01114610747578063a734f06e1461075c578063b0a7ef291461077157610381565b8063819faf7b116101ab578063819faf7b146106f3578063851954fa146107085780638aea49d21461071d5780638bdb2afa1461073257610381565b806375b5be2d146106b45780637a88bdbd146106c95780637e09b9c2146106de57610381565b80633e413bee116102b657806351f1985c1161025457806364ec4e5c1161022357806364ec4e5c1461066057806368e2a014146106755780636cbc4a6e1461068a57806375a8b0121461069f57610381565b806351f1985c1461060c5780635aa8fb48146106215780635ae51b82146106365780635c0cb4791461064b57610381565b8063423d03f911610290578063423d03f9146105b857806344211d62146105cd5780634a7101d5146105e25780634b57b0be146105f757610381565b80633e413bee1461057957806340ab7b8c1461058e5780634226a9b9146105a357610381565b806322320c98116103235780632f48ab7d116102fd5780632f48ab7d1461052557806334b4dabb1461053a578063372a26cb1461054f5780633ca5b2341461056457610381565b806322320c98146104e65780632d3b5207146104fb5780632e707bd21461051057610381565b80631388b4201161035f5780631388b4201461049257806313989140146104a75780632113240d146104bc57806321a360f5146104d157610381565b806305d8aa0a14610390578063085e2c5b146103b757806312dea16014610461575b3332141561038e57600080fd5b005b34801561039c57600080fd5b506103a56109ca565b60408051918252519081900360200190f35b3480156103c357600080fd5b50610406600480360360a08110156103da57600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608001356109d1565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b8381101561044c578181015183820152602001610434565b50505050905001935050505060405180910390f35b34801561046d57600080fd5b50610476610b1d565b604080516001600160a01b039092168252519081900360200190f35b34801561049e57600080fd5b50610476610b35565b3480156104b357600080fd5b506103a5610b4d565b3480156104c857600080fd5b506103a5610b53565b3480156104dd57600080fd5b506103a5610b59565b3480156104f257600080fd5b50610476610b62565b34801561050757600080fd5b506103a5610b7a565b34801561051c57600080fd5b506103a5610b83565b34801561053157600080fd5b50610476610b88565b34801561054657600080fd5b506103a5610ba0565b34801561055b57600080fd5b50610476610ba5565b34801561057057600080fd5b50610476610bbd565b34801561058557600080fd5b50610476610bd5565b34801561059a57600080fd5b50610476610be7565b3480156105af57600080fd5b506103a5610bff565b3480156105c457600080fd5b50610476610c07565b3480156105d957600080fd5b506103a5610c1f565b3480156105ee57600080fd5b506103a5610c24565b34801561060357600080fd5b50610476610c29565b34801561061857600080fd5b50610476610c41565b34801561062d57600080fd5b506103a5610c59565b34801561064257600080fd5b506103a5610c5f565b34801561065757600080fd5b506103a5610c65565b34801561066c57600080fd5b506103a5610c6a565b34801561068157600080fd5b506103a5610c71565b34801561069657600080fd5b506103a5610c78565b3480156106ab57600080fd5b506103a5610c7f565b3480156106c057600080fd5b50610476610c85565b3480156106d557600080fd5b506103a5610c98565b3480156106ea57600080fd5b506103a5610c9d565b3480156106ff57600080fd5b50610476610ca4565b34801561071457600080fd5b50610476610cbc565b34801561072957600080fd5b506103a5610cd4565b34801561073e57600080fd5b50610476610cdc565b34801561075357600080fd5b50610476610cf4565b34801561076857600080fd5b50610476610d0c565b34801561077d57600080fd5b506103a5610d24565b34801561079257600080fd5b506103a5610d2a565b3480156107a757600080fd5b50610476610d33565b3480156107bc57600080fd5b506103a5610d4b565b3480156107d157600080fd5b506103a5610d50565b3480156107e657600080fd5b50610476610d56565b3480156107fb57600080fd5b506103a5610d6e565b34801561081057600080fd5b506103a5610d75565b34801561082557600080fd5b506103a5610d7c565b34801561083a57600080fd5b50610476610d81565b34801561084f57600080fd5b506103a5610d99565b34801561086457600080fd5b506103a5610da1565b34801561087957600080fd5b50610476610da8565b34801561088e57600080fd5b50610476610dc0565b3480156108a357600080fd5b506103a5610dd8565b61038e600480360360c08110156108c257600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a08101608082013564010000000081111561090257600080fd5b82018360208201111561091457600080fd5b8035906020019184602083028401116401000000008311171561093657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610dde915050565b34801561098257600080fd5b50610476611000565b34801561099757600080fd5b506103a5611012565b3480156109ac57600080fd5b50610476611017565b3480156109c157600080fd5b5061047661102f565b6220000081565b600080546040805163085e2c5b60e01b81526001600160a01b03898116600483015288811660248301526044820188905260648201879052608482018690529151606093929092169163085e2c5b9160a4808201928792909190829003018186803b158015610a3f57600080fd5b505afa158015610a53573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610a7c57600080fd5b815160208301805160405192949293830192919084640100000000821115610aa357600080fd5b908301906020820185811115610ab857600080fd5b8251866020820283011164010000000082111715610ad557600080fd5b82525081516020918201928201910280838360005b83811015610b02578181015183820152602001610aea565b50505050905001604052505050915091509550959350505050565b7352ae12abe5d8bd778bd5397f99ca900624cfadd481565b73794e6e91555438afc3ccf1c5076a74f42133d08d81565b61200081565b61800081565b64020000000081565b73a5407eae9ba41422680e2e00537571bcc53efbfd81565b64010000000081565b608081565b73dac17f958d2ee523a2206206994597c13d831ec781565b604081565b7379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2781565b734fabb145d64652a948d72533023f6e7a623c7c5381565b600080516020613e9983398151915281565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c81565b630100000081565b7345f783cce6b7ff23b2ab2d70e416cdb7d6055f5181565b601081565b602081565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b73a2b47e3d5c44877cca798226b7b8118f9bfb7a5681565b61400081565b61080081565b600881565b6202000081565b6210000081565b6208000081565b61040081565b6e085d4780b73119b644ae5ecd22b37681565b600281565b6240000081565b73398ec7346dcd622edc5ae82352f02be94c62d11981565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce31581565b630400000081565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9581565b734ddc2d193948926d02f9b1fe9e1daa0718270ed581565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b7306af07097c9eeb7fd685c692751d5c66db49c21581565b600181565b61020081565b7357ab1ec28d129707052df4df418d58a2d46d5f5181565b6280000081565b6204000081565b600c81565b7352ea46506b9cc5ef470c5bf89f17dc28bb35d85c81565b630200000081565b6201000081565b73f6e2d7f616b67e46d708e4410746e9aab3a4c51881565b73818e6fecd516ecc3849daf6845e3ec868087b75581565b61010081565b846001600160a01b0316866001600160a01b03161415610dfd57610ff8565b610e05613dfd565b60405180610180016040528061103e81526020016112bf815260200161144a8152602001611467815260200161174081526020016118cb8152602001611a9c8152602001611cc18152602001611ef0815260200161211f81526020016122bd81526020016124698152509050600c83511115610eb25760405162461bcd60e51b8152600401808060200182810382526042815260200180613f196042913960600191505060405180910390fd5b600080805b8551811015610f10576000868281518110610ece57fe5b60200260200101511115610f0857610f02868281518110610eeb57fe5b6020026020010151846125d590919063ffffffff16565b92508091505b600101610eb7565b5060008211610f505760405162461bcd60e51b815260040180806020018281038252602f815260200180613e49602f913960400191505060405180910390fd5b8660005b8651811015610ff257868181518110610f6957fe5b602002602001015160001415610f7e57610fea565b6000610fb685610faa8a8581518110610f9357fe5b60200260200101518d61263890919063ffffffff16565b9063ffffffff61269116565b905083821415610fc35750815b8083039250610fe78c8c838986600c8110610fda57fe5b602002015163ffffffff16565b50505b600101610f54565b50505050505b505050505050565b600080516020613e2983398151915281565b600481565b733d9819210a31b4961b30ef54be2aed79b9c9cd3b81565b6000546001600160a01b031681565b6000816110536001600160a01b0386166126d3565b61118357604080516303795fb160e11b81526001600160a01b0387166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156110b557600080fd5b505afa1580156110c9573d6000803e3d6000fd5b505050506040513d60208110156110df57600080fd5b505190506001600160a01b03811615611181576110fc868261270f565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561115257600080fd5b505af1158015611166573d6000803e3d6000fd5b505050506040513d602081101561117c57600080fd5b505191505b505b611195846001600160a01b03166126d3565b6112b557604080516303795fb160e11b81526001600160a01b0386166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156111f757600080fd5b505afa15801561120b573d6000803e3d6000fd5b505050506040513d602081101561122157600080fd5b505190506001600160a01b038116156112b357806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b15801561128357600080fd5b505af1158015611297573d6000803e3d6000fd5b50505050506040513d60208110156112ae57600080fd5b505191505b505b90505b9392505050565b60006112df8473818e6fecd516ecc3849daf6845e3ec868087b75561270f565b73818e6fecd516ecc3849daf6845e3ec868087b7556329589f6161130b6001600160a01b0387166126d3565b611316576000611318565b835b61132a876001600160a01b03166126d3565b611334578661134a565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b8561135d886001600160a01b03166126d3565b611367578761137d565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b15801561141557600080fd5b505af1158015611429573d6000803e3d6000fd5b50505050506040513d602081101561144057600080fd5b5051949350505050565b6000806114588585856127c8565b9050600081116112b557600080fd5b600061147b846001600160a01b03166126d3565b156114e95773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156114cf57600080fd5b505af11580156114e3573d6000803e3d6000fd5b50505050505b6115386114fe856001600160a01b03166126d3565b611508578461151e565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b73794e6e91555438afc3ccf1c5076a74f42133d08d61270f565b600073794e6e91555438afc3ccf1c5076a74f42133d08d630621b4f66115666001600160a01b0388166126d3565b6115705786611586565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b85611599886001600160a01b03166126d3565b6115a357876115b9565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b15801561161757600080fd5b505af115801561162b573d6000803e3d6000fd5b505050506040513d602081101561164157600080fd5b505190506116576001600160a01b0385166126d3565b156112b557604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b1580156116b557600080fd5b505afa1580156116c9573d6000803e3d6000fd5b505050506040513d60208110156116df57600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561171f57600080fd5b505af1158015611733573d6000803e3d6000fd5b5050505090509392505050565b6000806001600160a01b038516600080516020613e998339815191521461176857600061176b565b60025b6001600160a01b038616600080516020613e2983398151915214611790576000611793565b60015b0160ff1690506000600080516020613e998339815191526001600160a01b038616146117c05760006117c3565b60025b6001600160a01b038616600080516020613e29833981519152146117e85760006117eb565b60015b0160ff16905081600f0b60001480611806575080600f0b6000145b15611816576000925050506112b8565b6118348673a2b47e3d5c44877cca798226b7b8118f9bfb7a5661270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a2b47e3d5c44877cca798226b7b8118f9bfb7a569263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b505af11580156118be573d6000803e3d6000fd5b5050505050509392505050565b6000806001600160a01b03851673dac17f958d2ee523a2206206994597c13d831ec7146118f95760006118fc565b60035b6001600160a01b038616600080516020613e9983398151915214611921576000611924565b60025b6001600160a01b038716600080516020613e298339815191521461194957600061194c565b60015b010160ff169050600073dac17f958d2ee523a2206206994597c13d831ec76001600160a01b0316856001600160a01b03161461198957600061198c565b60035b6001600160a01b038616600080516020613e99833981519152146119b15760006119b4565b60025b6001600160a01b038716600080516020613e29833981519152146119d95760006119dc565b60015b010160ff16905081600f0b600014806119f8575080600f0b6000145b15611a08576000925050506112b8565b611a26867352ea46506b9cc5ef470c5bf89f17dc28bb35d85c61270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517352ea46506b9cc5ef470c5bf89f17dc28bb35d85c9263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000806001600160a01b0385166e085d4780b73119b644ae5ecd22b37614611ac5576000611ac8565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611af3576000611af6565b60035b6001600160a01b038716600080516020613e9983398151915214611b1b576000611b1e565b60025b6001600160a01b038816600080516020613e2983398151915214611b43576000611b46565b60015b01010160ff16905060006e085d4780b73119b644ae5ecd22b3766001600160a01b0316856001600160a01b031614611b7f576000611b82565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611bad576000611bb0565b60035b6001600160a01b038716600080516020613e9983398151915214611bd5576000611bd8565b60025b6001600160a01b038816600080516020613e2983398151915214611bfd576000611c00565b60015b01010160ff16905081600f0b60001480611c1d575080600f0b6000145b15611c2d576000925050506112b8565b611c4b867345f783cce6b7ff23b2ab2d70e416cdb7d6055f5161270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517345f783cce6b7ff23b2ab2d70e416cdb7d6055f519263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000806001600160a01b038516734fabb145d64652a948d72533023f6e7a623c7c5314611cef576000611cf2565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611d1d576000611d20565b60035b6001600160a01b038716600080516020613e9983398151915214611d45576000611d48565b60025b6001600160a01b038816600080516020613e2983398151915214611d6d576000611d70565b60015b01010160ff1690506000734fabb145d64652a948d72533023f6e7a623c7c536001600160a01b0316856001600160a01b031614611dae576000611db1565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611ddc576000611ddf565b60035b6001600160a01b038716600080516020613e9983398151915214611e04576000611e07565b60025b6001600160a01b038816600080516020613e2983398151915214611e2c576000611e2f565b60015b01010160ff16905081600f0b60001480611e4c575080600f0b6000145b15611e5c576000925050506112b8565b611e7a867379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2761270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517379a8c46dea5ada233abaffd40f3a0a2b1e5a4f279263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000806001600160a01b0385167357ab1ec28d129707052df4df418d58a2d46d5f5114611f1e576000611f21565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611f4c576000611f4f565b60035b6001600160a01b038716600080516020613e9983398151915214611f74576000611f77565b60025b6001600160a01b038816600080516020613e2983398151915214611f9c576000611f9f565b60015b01010160ff16905060007357ab1ec28d129707052df4df418d58a2d46d5f516001600160a01b0316856001600160a01b031614611fdd576000611fe0565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec71461200b57600061200e565b60035b6001600160a01b038716600080516020613e9983398151915214612033576000612036565b60025b6001600160a01b038816600080516020613e298339815191521461205b57600061205e565b60015b01010160ff16905081600f0b6000148061207b575080600f0b6000145b1561208b576000925050506112b8565b6120a98673a5407eae9ba41422680e2e00537571bcc53efbfd61270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a5407eae9ba41422680e2e00537571bcc53efbfd9263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000612133846001600160a01b03166126d3565b6121ec57600061214285612bb6565b905061214e858261270f565b806001600160a01b031663a0712d68846040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561219457600080fd5b505af11580156121a8573d6000803e3d6000fd5b505050506040513d60208110156121be57600080fd5b506121e4905081856121df6001600160a01b0383163063ffffffff612da616565b61103e565b9150506112b8565b6121fe836001600160a01b03166126d3565b6122b357600061220d84612bb6565b9050600061221c86838661103e565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561226457600080fd5b505af1158015612278573d6000803e3d6000fd5b505050506040513d602081101561228e57600080fd5b506122aa90506001600160a01b0386163063ffffffff612da616565b925050506112b8565b5060009392505050565b60006001600160a01b038416600080516020613e2983398151915214156123a0576122fc847306af07097c9eeb7fd685c692751d5c66db49c21561270f565b60408051633b4da69f60e01b81523060048201526024810184905290517306af07097c9eeb7fd685c692751d5c66db49c21591633b4da69f91604480830192600092919082900301818387803b15801561235557600080fd5b505af1158015612369573d6000803e3d6000fd5b5061239992507306af07097c9eeb7fd685c692751d5c66db49c21591508590506121df823063ffffffff612da616565b90506112b8565b6001600160a01b038316600080516020613e2983398151915214156122b35760006123e0857306af07097c9eeb7fd685c692751d5c66db49c2158561103e565b6040805163ef693bed60e01b81523060048201526024810183905290519192507306af07097c9eeb7fd685c692751d5c66db49c2159163ef693bed9160448082019260009290919082900301818387803b15801561243d57600080fd5b505af1158015612451573d6000803e3d6000fd5b506121e4925050506001600160a01b03851630612da6565b600061247d846001600160a01b03166126d3565b61253b57600061248c85612e50565b9050612498858261270f565b60408051636968703360e11b81526001600160a01b03871660048201526024810185905261044d6044820152905173398ec7346dcd622edc5ae82352f02be94c62d1199163d2d0e06691606480830192600092919082900301818387803b15801561250257600080fd5b505af1158015612516573d6000803e3d6000fd5b505050506121e481856121df30856001600160a01b0316612da690919063ffffffff16565b61254d836001600160a01b03166126d3565b6122b357600061255c84612e50565b9050600061256b86838661103e565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156125b357600080fd5b505af11580156125c7573d6000803e3d6000fd5b5050505080925050506112b8565b60008282018381101561262f576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60008261264757506000612632565b8282028284828161265457fe5b041461262f5760405162461bcd60e51b8152600401808060200182810382526021815260200180613e786021913960400191505060405180910390fd5b600061262f83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250613273565b60006001600160a01b038216158061270757506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b90505b919050565b612721826001600160a01b03166126d3565b6127c45760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b15801561277657600080fd5b505afa15801561278a573d6000803e3d6000fd5b505050506040513d60208110156127a057600080fd5b5051901c6127c4576127c46001600160a01b0383168260001963ffffffff61331516565b5050565b60006127dc846001600160a01b03166126d3565b1561284a5773c0829421c1d260bd3cb3e0f06cfe2d52db2ce3156001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561283057600080fd5b505af1158015612844573d6000803e3d6000fd5b50505050505b60007352ae12abe5d8bd778bd5397f99ca900624cfadd46001600160a01b031663bb34534c6040518163ffffffff1660e01b815260040180806c42616e636f724e6574776f726b60981b815250602001905060206040518083038186803b1580156128b457600080fd5b505afa1580156128c8573d6000803e3d6000fd5b505050506040513d60208110156128de57600080fd5b5051905060606128ee86866133eb565b905061292b612905876001600160a01b03166126d3565b61290f5786612925565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b8361270f565b60006060836001600160a01b03166216e360856001600160a01b031663c7ba24bc905060e01b8589600160405160240180806020018481526020018360ff168152602001828103825285818151815260200191508051906020019060200280838360005b838110156129a757818101518382015260200161298f565b50505050905001945050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0383818316178352505050506040518082805190602001908083835b60208310612a165780518252601f1990920191602091820191016129f7565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038160008787f1925050503d8060008114612a79576040519150601f19603f3d011682016040523d82523d6000602084013e612a7e565b606091505b5091509150600082612a91576000612aa9565b818060200190516020811015612aa657600080fd5b50515b9050612abd886001600160a01b03166126d3565b8015612ac95750600081115b15612baa57604080516370a0823160e01b8152306004820152905173c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591632e1a7d4d9183916370a08231916024808301926020929190829003018186803b158015612b2757600080fd5b505afa158015612b3b573d6000803e3d6000fd5b505050506040513d6020811015612b5157600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015612b9157600080fd5b505af1158015612ba5573d6000803e3d6000fd5b505050505b98975050505050505050565b6000612bca826001600160a01b03166126d3565b15612bea5750734ddc2d193948926d02f9b1fe9e1daa0718270ed561270a565b6001600160a01b038216600080516020613e298339815191521415612c245750735d3a536e4d6dbd6114cc1ead35777bab948e364361270a565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415612c645750736c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e61270a565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e8621415612ca4575073158079ee67fce2f58472a96584a73c7ab9ac95c161270a565b6001600160a01b038216600080516020613e998339815191521415612cde57507339aa39c021dfbae8fac545936693ac917d5e756361270a565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415612d1e575073c11b1268c1a384e55c48c2391d8d480264a3a7f461270a565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415612d5e575073b3319f5d18bc0d84dd1b4825dcde5d5f7266d40761270a565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec71415612d9e575073f650c3d88d12db855b8bf7d11be6c55a4e07dcc961270a565b506000919050565b6000612db1836126d3565b15612dc757506001600160a01b03811631612632565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015612e1d57600080fd5b505afa158015612e31573d6000803e3d6000fd5b505050506040513d6020811015612e4757600080fd5b50519050612632565b6000612e64826001600160a01b03166126d3565b15612e845750733a3a65aab0dd2a17e3f1947ba16138cd37d08c0461270a565b6001600160a01b038216600080516020613e298339815191521415612ebe575073fc1e690f61efd961294b3e1ce3313fbd8aa4f85d61270a565b6001600160a01b038216600080516020613e998339815191521415612ef85750739ba00d6856a4edf4665bca2c2309936572473b7e61270a565b6001600160a01b0382167357ab1ec28d129707052df4df418d58a2d46d5f511415612f38575073625ae63000f46200499120b906716420bd05924061270a565b6001600160a01b038216734fabb145d64652a948d72533023f6e7a623c7c531415612f785750736ee0f7bb50a54ab5253da0667b0dc2ee526c30a861270a565b6001600160a01b0382166e085d4780b73119b644ae5ecd22b3761415612fb35750734da9b813057d04baef4e5800e36083717b4a034161270a565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec71415612ff357507371fc860f7d3a592a4a98740e39db31d25db65ae861270a565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415613033575073e1ba0fb44ccb0d11b80f92f4f8ed94ca3ff51d0061270a565b6001600160a01b03821673dd974d5c2e2928dea5f71b9825b8b646686bd20014156130735750739d91be44c06d373a8a226e1f3b146956083803eb61270a565b6001600160a01b0382167380fb784b7ed66730e8b1dbd9820afd29931aab0314156130b35750737d2d3688df45ce7c552e19c27e007673da9204b861270a565b6001600160a01b03821673514910771af9ca656af840dff83e8264ecf986ca14156130f3575073a64bd6c70cb9051f6a9ba1f163fdc07e0dfb5f8461270a565b6001600160a01b038216730f5d2fb29fb7d3cfee444a200298f468908cc94214156131335750736fce4a401b6b80ace52baaefe4421bd188e76f6f61270a565b6001600160a01b038216739f8f72aa9304c8b593d555f12ef6589cc3a579a214156131735750737deb5e830be29f91e298ba5ff1356bb7f814699861270a565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e86214156131b357507371010a9d003445ac60c4e6a7017c1e89a477b43861270a565b6001600160a01b03821673c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f14156131f3575073328c4c80bc7aca0834db37e6600a6c49e12da4de61270a565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415613233575073fc4b8ed459e00e5400be803a9bb3954234fd50e361270a565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415612d9e5750736fb0855c404e09c47c3fbca25f08d4e41f9f062f61270a565b600081836132ff5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156132c45781810151838201526020016132ac565b50505050905090810190601f1680156132f15780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161330b57fe5b0495945050505050565b61331e836126d3565b6133e6576000811180156133ac575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561337e57600080fd5b505afa158015613392573d6000803e3d6000fd5b505050506040513d60208110156133a857600080fd5b5051115b156133cc576133cc6001600160a01b03841683600063ffffffff613af516565b6133e66001600160a01b038416838363ffffffff613af516565b505050565b6060816001600160a01b0316836001600160a01b0316141561341c5750604080516000815260208101909152612632565b61342e836001600160a01b03166126d3565b1561344b5773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31592505b61345d826001600160a01b03166126d3565b1561347a5773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591505b6001600160a01b038316731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14806134c157506001600160a01b038216731f573d6fb3f13d689ff844b4ce37794d79a7ff1c145b156134ec5760408051600380825260808201909252906020820160608038833901905050905061350e565b60408051600580825260c08201909252906020820160a0803883390190505090505b6000806001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c146136d7576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b61356b6001600160a01b038b166126d3565b613575578961358b565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106136095780518252601f1990920191602091820191016135ea565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d806000811461366a576040519150601f19603f3d011682016040523d82523d6000602084013e61366f565b606091505b5091509150816136975760408051600080825260208201909252905b50945050505050612632565b8080602001905160208110156136ac57600080fd5b505193506001600160a01b0384166136d457604080516000808252602082019092529061368b565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14613895576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6137316001600160a01b038a166126d3565b61373b5788613751565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106137cf5780518252601f1990920191602091820191016137b0565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613830576040519150601f19603f3d011682016040523d82523d6000602084013e613835565b606091505b50915091508161385557604080516000808252602082019092529061368b565b80806020019051602081101561386a57600080fd5b505192506001600160a01b03831661389257604080516000808252602082019092529061368b565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156139585784836000815181106138c857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505081836001815181106138f657fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360028151811061393857fe5b6001600160a01b0390921660209283029190910190910152506126329050565b6001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156139fb57731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360008151811061399f57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505080836001815181106139cd57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050838360028151811061393857fe5b8483600081518110613a0957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508183600181518110613a3757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c83600281518110613a7957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508083600381518110613aa757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508383600481518110613ad557fe5b6001600160a01b0390921660209283029190910190910152505092915050565b801580613b7b575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b158015613b4d57600080fd5b505afa158015613b61573d6000803e3d6000fd5b505050506040513d6020811015613b7757600080fd5b5051155b613bb65760405162461bcd60e51b8152600401808060200182810382526036815260200180613ee36036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526133e6908490613c15826001600160a01b0316613dc1565b613c66576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310613ca45780518252601f199092019160209182019101613c85565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613d06576040519150601f19603f3d011682016040523d82523d6000602084013e613d0b565b606091505b509150915081613d62576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115613dbb57808060200190516020811015613d7e57600080fd5b5051613dbb5760405162461bcd60e51b815260040180806020018281038252602a815260200180613eb9602a913960400191505060405180910390fd5b50505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590613df557508115155b949350505050565b604051806101800160405280600c905b613e26815260200190600190039081613e0d5790505090565bfefe0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f73536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e63654f6e6553706c69743a20446973747269627574696f6e2061727261792073686f756c64206e6f74206578636565642072657365727665732061727261792073697a65a265627a7a72315820902ecc0ac5a33f82f02692c6d5f2c405a313ce3f3e5ddbefe65df23e2ee54cdb64736f6c63430005110032 \ No newline at end of file +608060405234801561001057600080fd5b50604051613ff4380380613ff48339818101604052602081101561003357600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055613f8f806100656000396000f3fe6080604052600436106103815760003560e01c806375b5be2d116101d1578063c925777511610102578063d70a2d1f116100a0578063f4b9fa751161006f578063f4b9fa7514610976578063f56e281f1461098b578063f69e2046146109a0578063fbe4ed95146109b557610381565b8063d70a2d1f1461086d578063d77366a414610882578063dc1536b214610897578063e2a7515e146108ac57610381565b8063cc26e9fc116100dc578063cc26e9fc14610819578063cede5f6a1461082e578063d1aee5e314610843578063d393c3e91461085857610381565b8063c9257775146107da578063c989b667146107ef578063c9b42c671461080457610381565b8063a1b4d0111161016f578063b3bc784411610149578063b3bc784414610786578063b69d04561461079b578063c762a46c146107b0578063c77b9de6146107c557610381565b8063a1b4d01114610747578063a734f06e1461075c578063b0a7ef291461077157610381565b8063819faf7b116101ab578063819faf7b146106f3578063851954fa146107085780638aea49d21461071d5780638bdb2afa1461073257610381565b806375b5be2d146106b45780637a88bdbd146106c95780637e09b9c2146106de57610381565b80633e413bee116102b657806351f1985c1161025457806364ec4e5c1161022357806364ec4e5c1461066057806368e2a014146106755780636cbc4a6e1461068a57806375a8b0121461069f57610381565b806351f1985c1461060c5780635aa8fb48146106215780635ae51b82146106365780635c0cb4791461064b57610381565b8063423d03f911610290578063423d03f9146105b857806344211d62146105cd5780634a7101d5146105e25780634b57b0be146105f757610381565b80633e413bee1461057957806340ab7b8c1461058e5780634226a9b9146105a357610381565b806322320c98116103235780632f48ab7d116102fd5780632f48ab7d1461052557806334b4dabb1461053a578063372a26cb1461054f5780633ca5b2341461056457610381565b806322320c98146104e65780632d3b5207146104fb5780632e707bd21461051057610381565b80631388b4201161035f5780631388b4201461049257806313989140146104a75780632113240d146104bc57806321a360f5146104d157610381565b806305d8aa0a14610390578063085e2c5b146103b757806312dea16014610461575b3332141561038e57600080fd5b005b34801561039c57600080fd5b506103a56109ca565b60408051918252519081900360200190f35b3480156103c357600080fd5b50610406600480360360a08110156103da57600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608001356109d1565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b8381101561044c578181015183820152602001610434565b50505050905001935050505060405180910390f35b34801561046d57600080fd5b50610476610b1d565b604080516001600160a01b039092168252519081900360200190f35b34801561049e57600080fd5b50610476610b35565b3480156104b357600080fd5b506103a5610b4d565b3480156104c857600080fd5b506103a5610b53565b3480156104dd57600080fd5b506103a5610b59565b3480156104f257600080fd5b50610476610b62565b34801561050757600080fd5b506103a5610b7a565b34801561051c57600080fd5b506103a5610b83565b34801561053157600080fd5b50610476610b88565b34801561054657600080fd5b506103a5610ba0565b34801561055b57600080fd5b50610476610ba5565b34801561057057600080fd5b50610476610bbd565b34801561058557600080fd5b50610476610bd5565b34801561059a57600080fd5b50610476610be7565b3480156105af57600080fd5b506103a5610bff565b3480156105c457600080fd5b50610476610c07565b3480156105d957600080fd5b506103a5610c1f565b3480156105ee57600080fd5b506103a5610c24565b34801561060357600080fd5b50610476610c29565b34801561061857600080fd5b50610476610c41565b34801561062d57600080fd5b506103a5610c59565b34801561064257600080fd5b506103a5610c5f565b34801561065757600080fd5b506103a5610c65565b34801561066c57600080fd5b506103a5610c6a565b34801561068157600080fd5b506103a5610c71565b34801561069657600080fd5b506103a5610c78565b3480156106ab57600080fd5b506103a5610c7f565b3480156106c057600080fd5b50610476610c85565b3480156106d557600080fd5b506103a5610c98565b3480156106ea57600080fd5b506103a5610c9d565b3480156106ff57600080fd5b50610476610ca4565b34801561071457600080fd5b50610476610cbc565b34801561072957600080fd5b506103a5610cd4565b34801561073e57600080fd5b50610476610cdc565b34801561075357600080fd5b50610476610cf4565b34801561076857600080fd5b50610476610d0c565b34801561077d57600080fd5b506103a5610d24565b34801561079257600080fd5b506103a5610d2a565b3480156107a757600080fd5b50610476610d33565b3480156107bc57600080fd5b506103a5610d4b565b3480156107d157600080fd5b506103a5610d50565b3480156107e657600080fd5b50610476610d56565b3480156107fb57600080fd5b506103a5610d6e565b34801561081057600080fd5b506103a5610d75565b34801561082557600080fd5b506103a5610d7c565b34801561083a57600080fd5b50610476610d81565b34801561084f57600080fd5b506103a5610d99565b34801561086457600080fd5b506103a5610da1565b34801561087957600080fd5b50610476610da8565b34801561088e57600080fd5b50610476610dc0565b3480156108a357600080fd5b506103a5610dd8565b61038e600480360360c08110156108c257600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a08101608082013564010000000081111561090257600080fd5b82018360208201111561091457600080fd5b8035906020019184602083028401116401000000008311171561093657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610dde915050565b34801561098257600080fd5b50610476611000565b34801561099757600080fd5b506103a5611012565b3480156109ac57600080fd5b50610476611017565b3480156109c157600080fd5b5061047661102f565b6220000081565b600080546040805163085e2c5b60e01b81526001600160a01b03898116600483015288811660248301526044820188905260648201879052608482018690529151606093929092169163085e2c5b9160a4808201928792909190829003018186803b158015610a3f57600080fd5b505afa158015610a53573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610a7c57600080fd5b815160208301805160405192949293830192919084640100000000821115610aa357600080fd5b908301906020820185811115610ab857600080fd5b8251866020820283011164010000000082111715610ad557600080fd5b82525081516020918201928201910280838360005b83811015610b02578181015183820152602001610aea565b50505050905001604052505050915091509550959350505050565b7352ae12abe5d8bd778bd5397f99ca900624cfadd481565b73794e6e91555438afc3ccf1c5076a74f42133d08d81565b61200081565b61800081565b64020000000081565b73a5407eae9ba41422680e2e00537571bcc53efbfd81565b64010000000081565b608081565b73dac17f958d2ee523a2206206994597c13d831ec781565b604081565b7379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2781565b734fabb145d64652a948d72533023f6e7a623c7c5381565b600080516020613e9983398151915281565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c81565b630100000081565b7345f783cce6b7ff23b2ab2d70e416cdb7d6055f5181565b601081565b602081565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b73a2b47e3d5c44877cca798226b7b8118f9bfb7a5681565b61400081565b61080081565b600881565b6202000081565b6210000081565b6208000081565b61040081565b6e085d4780b73119b644ae5ecd22b37681565b600281565b6240000081565b73398ec7346dcd622edc5ae82352f02be94c62d11981565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce31581565b630400000081565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9581565b734ddc2d193948926d02f9b1fe9e1daa0718270ed581565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b7306af07097c9eeb7fd685c692751d5c66db49c21581565b600181565b61020081565b7357ab1ec28d129707052df4df418d58a2d46d5f5181565b6280000081565b6204000081565b600c81565b7352ea46506b9cc5ef470c5bf89f17dc28bb35d85c81565b630200000081565b6201000081565b73f6e2d7f616b67e46d708e4410746e9aab3a4c51881565b73818e6fecd516ecc3849daf6845e3ec868087b75581565b61010081565b846001600160a01b0316866001600160a01b03161415610dfd57610ff8565b610e05613dfd565b60405180610180016040528061103e81526020016112bf815260200161144a8152602001611467815260200161174081526020016118cb8152602001611a9c8152602001611cc18152602001611ef0815260200161211f81526020016122bd81526020016124698152509050600c83511115610eb25760405162461bcd60e51b8152600401808060200182810382526042815260200180613f196042913960600191505060405180910390fd5b600080805b8551811015610f10576000868281518110610ece57fe5b60200260200101511115610f0857610f02868281518110610eeb57fe5b6020026020010151846125d590919063ffffffff16565b92508091505b600101610eb7565b5060008211610f505760405162461bcd60e51b815260040180806020018281038252602f815260200180613e49602f913960400191505060405180910390fd5b8660005b8651811015610ff257868181518110610f6957fe5b602002602001015160001415610f7e57610fea565b6000610fb685610faa8a8581518110610f9357fe5b60200260200101518d61263890919063ffffffff16565b9063ffffffff61269116565b905083821415610fc35750815b8083039250610fe78c8c838986600c8110610fda57fe5b602002015163ffffffff16565b50505b600101610f54565b50505050505b505050505050565b600080516020613e2983398151915281565b600481565b733d9819210a31b4961b30ef54be2aed79b9c9cd3b81565b6000546001600160a01b031681565b6000816110536001600160a01b0386166126d3565b61118357604080516303795fb160e11b81526001600160a01b0387166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156110b557600080fd5b505afa1580156110c9573d6000803e3d6000fd5b505050506040513d60208110156110df57600080fd5b505190506001600160a01b03811615611181576110fc868261270f565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561115257600080fd5b505af1158015611166573d6000803e3d6000fd5b505050506040513d602081101561117c57600080fd5b505191505b505b611195846001600160a01b03166126d3565b6112b557604080516303795fb160e11b81526001600160a01b0386166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156111f757600080fd5b505afa15801561120b573d6000803e3d6000fd5b505050506040513d602081101561122157600080fd5b505190506001600160a01b038116156112b357806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b15801561128357600080fd5b505af1158015611297573d6000803e3d6000fd5b50505050506040513d60208110156112ae57600080fd5b505191505b505b90505b9392505050565b60006112df8473818e6fecd516ecc3849daf6845e3ec868087b75561270f565b73818e6fecd516ecc3849daf6845e3ec868087b7556329589f6161130b6001600160a01b0387166126d3565b611316576000611318565b835b61132a876001600160a01b03166126d3565b611334578661134a565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b8561135d886001600160a01b03166126d3565b611367578761137d565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b15801561141557600080fd5b505af1158015611429573d6000803e3d6000fd5b50505050506040513d602081101561144057600080fd5b5051949350505050565b6000806114588585856127c8565b9050600081116112b557600080fd5b600061147b846001600160a01b03166126d3565b156114e95773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156114cf57600080fd5b505af11580156114e3573d6000803e3d6000fd5b50505050505b6115386114fe856001600160a01b03166126d3565b611508578461151e565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b73794e6e91555438afc3ccf1c5076a74f42133d08d61270f565b600073794e6e91555438afc3ccf1c5076a74f42133d08d630621b4f66115666001600160a01b0388166126d3565b6115705786611586565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b85611599886001600160a01b03166126d3565b6115a357876115b9565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b15801561161757600080fd5b505af115801561162b573d6000803e3d6000fd5b505050506040513d602081101561164157600080fd5b505190506116576001600160a01b0385166126d3565b156112b557604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b1580156116b557600080fd5b505afa1580156116c9573d6000803e3d6000fd5b505050506040513d60208110156116df57600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561171f57600080fd5b505af1158015611733573d6000803e3d6000fd5b5050505090509392505050565b6000806001600160a01b038516600080516020613e998339815191521461176857600061176b565b60025b6001600160a01b038616600080516020613e2983398151915214611790576000611793565b60015b0160ff1690506000600080516020613e998339815191526001600160a01b038616146117c05760006117c3565b60025b6001600160a01b038616600080516020613e29833981519152146117e85760006117eb565b60015b0160ff16905081600f0b60001480611806575080600f0b6000145b15611816576000925050506112b8565b6118348673a2b47e3d5c44877cca798226b7b8118f9bfb7a5661270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a2b47e3d5c44877cca798226b7b8118f9bfb7a569263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b505af11580156118be573d6000803e3d6000fd5b5050505050509392505050565b6000806001600160a01b03851673dac17f958d2ee523a2206206994597c13d831ec7146118f95760006118fc565b60035b6001600160a01b038616600080516020613e9983398151915214611921576000611924565b60025b6001600160a01b038716600080516020613e298339815191521461194957600061194c565b60015b010160ff169050600073dac17f958d2ee523a2206206994597c13d831ec76001600160a01b0316856001600160a01b03161461198957600061198c565b60035b6001600160a01b038616600080516020613e99833981519152146119b15760006119b4565b60025b6001600160a01b038716600080516020613e29833981519152146119d95760006119dc565b60015b010160ff16905081600f0b600014806119f8575080600f0b6000145b15611a08576000925050506112b8565b611a26867352ea46506b9cc5ef470c5bf89f17dc28bb35d85c61270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517352ea46506b9cc5ef470c5bf89f17dc28bb35d85c9263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000806001600160a01b0385166e085d4780b73119b644ae5ecd22b37614611ac5576000611ac8565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611af3576000611af6565b60035b6001600160a01b038716600080516020613e9983398151915214611b1b576000611b1e565b60025b6001600160a01b038816600080516020613e2983398151915214611b43576000611b46565b60015b01010160ff16905060006e085d4780b73119b644ae5ecd22b3766001600160a01b0316856001600160a01b031614611b7f576000611b82565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611bad576000611bb0565b60035b6001600160a01b038716600080516020613e9983398151915214611bd5576000611bd8565b60025b6001600160a01b038816600080516020613e2983398151915214611bfd576000611c00565b60015b01010160ff16905081600f0b60001480611c1d575080600f0b6000145b15611c2d576000925050506112b8565b611c4b867345f783cce6b7ff23b2ab2d70e416cdb7d6055f5161270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517345f783cce6b7ff23b2ab2d70e416cdb7d6055f519263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000806001600160a01b038516734fabb145d64652a948d72533023f6e7a623c7c5314611cef576000611cf2565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611d1d576000611d20565b60035b6001600160a01b038716600080516020613e9983398151915214611d45576000611d48565b60025b6001600160a01b038816600080516020613e2983398151915214611d6d576000611d70565b60015b01010160ff1690506000734fabb145d64652a948d72533023f6e7a623c7c536001600160a01b0316856001600160a01b031614611dae576000611db1565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611ddc576000611ddf565b60035b6001600160a01b038716600080516020613e9983398151915214611e04576000611e07565b60025b6001600160a01b038816600080516020613e2983398151915214611e2c576000611e2f565b60015b01010160ff16905081600f0b60001480611e4c575080600f0b6000145b15611e5c576000925050506112b8565b611e7a867379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2761270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517379a8c46dea5ada233abaffd40f3a0a2b1e5a4f279263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000806001600160a01b0385167357ab1ec28d129707052df4df418d58a2d46d5f5114611f1e576000611f21565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611f4c576000611f4f565b60035b6001600160a01b038716600080516020613e9983398151915214611f74576000611f77565b60025b6001600160a01b038816600080516020613e2983398151915214611f9c576000611f9f565b60015b01010160ff16905060007357ab1ec28d129707052df4df418d58a2d46d5f516001600160a01b0316856001600160a01b031614611fdd576000611fe0565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec71461200b57600061200e565b60035b6001600160a01b038716600080516020613e9983398151915214612033576000612036565b60025b6001600160a01b038816600080516020613e298339815191521461205b57600061205e565b60015b01010160ff16905081600f0b6000148061207b575080600f0b6000145b1561208b576000925050506112b8565b6120a98673a5407eae9ba41422680e2e00537571bcc53efbfd61270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a5407eae9ba41422680e2e00537571bcc53efbfd9263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000612133846001600160a01b03166126d3565b6121ec57600061214285612bb6565b905061214e858261270f565b806001600160a01b031663a0712d68846040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561219457600080fd5b505af11580156121a8573d6000803e3d6000fd5b505050506040513d60208110156121be57600080fd5b506121e4905081856121df6001600160a01b0383163063ffffffff612da616565b61103e565b9150506112b8565b6121fe836001600160a01b03166126d3565b6122b357600061220d84612bb6565b9050600061221c86838661103e565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561226457600080fd5b505af1158015612278573d6000803e3d6000fd5b505050506040513d602081101561228e57600080fd5b506122aa90506001600160a01b0386163063ffffffff612da616565b925050506112b8565b5060009392505050565b60006001600160a01b038416600080516020613e2983398151915214156123a0576122fc847306af07097c9eeb7fd685c692751d5c66db49c21561270f565b60408051633b4da69f60e01b81523060048201526024810184905290517306af07097c9eeb7fd685c692751d5c66db49c21591633b4da69f91604480830192600092919082900301818387803b15801561235557600080fd5b505af1158015612369573d6000803e3d6000fd5b5061239992507306af07097c9eeb7fd685c692751d5c66db49c21591508590506121df823063ffffffff612da616565b90506112b8565b6001600160a01b038316600080516020613e2983398151915214156122b35760006123e0857306af07097c9eeb7fd685c692751d5c66db49c2158561103e565b6040805163ef693bed60e01b81523060048201526024810183905290519192507306af07097c9eeb7fd685c692751d5c66db49c2159163ef693bed9160448082019260009290919082900301818387803b15801561243d57600080fd5b505af1158015612451573d6000803e3d6000fd5b506121e4925050506001600160a01b03851630612da6565b600061247d846001600160a01b03166126d3565b61253b57600061248c85612e50565b9050612498858261270f565b60408051636968703360e11b81526001600160a01b03871660048201526024810185905261044d6044820152905173398ec7346dcd622edc5ae82352f02be94c62d1199163d2d0e06691606480830192600092919082900301818387803b15801561250257600080fd5b505af1158015612516573d6000803e3d6000fd5b505050506121e481856121df30856001600160a01b0316612da690919063ffffffff16565b61254d836001600160a01b03166126d3565b6122b357600061255c84612e50565b9050600061256b86838661103e565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156125b357600080fd5b505af11580156125c7573d6000803e3d6000fd5b5050505080925050506112b8565b60008282018381101561262f576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60008261264757506000612632565b8282028284828161265457fe5b041461262f5760405162461bcd60e51b8152600401808060200182810382526021815260200180613e786021913960400191505060405180910390fd5b600061262f83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250613273565b60006001600160a01b038216158061270757506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b90505b919050565b612721826001600160a01b03166126d3565b6127c45760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b15801561277657600080fd5b505afa15801561278a573d6000803e3d6000fd5b505050506040513d60208110156127a057600080fd5b5051901c6127c4576127c46001600160a01b0383168260001963ffffffff61331516565b5050565b60006127dc846001600160a01b03166126d3565b1561284a5773c0829421c1d260bd3cb3e0f06cfe2d52db2ce3156001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561283057600080fd5b505af1158015612844573d6000803e3d6000fd5b50505050505b60007352ae12abe5d8bd778bd5397f99ca900624cfadd46001600160a01b031663bb34534c6040518163ffffffff1660e01b815260040180806c42616e636f724e6574776f726b60981b815250602001905060206040518083038186803b1580156128b457600080fd5b505afa1580156128c8573d6000803e3d6000fd5b505050506040513d60208110156128de57600080fd5b5051905060606128ee86866133eb565b905061292b612905876001600160a01b03166126d3565b61290f5786612925565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b8361270f565b60006060836001600160a01b03166216e360856001600160a01b031663c7ba24bc905060e01b8589600160405160240180806020018481526020018360ff168152602001828103825285818151815260200191508051906020019060200280838360005b838110156129a757818101518382015260200161298f565b50505050905001945050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0383818316178352505050506040518082805190602001908083835b60208310612a165780518252601f1990920191602091820191016129f7565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038160008787f1925050503d8060008114612a79576040519150601f19603f3d011682016040523d82523d6000602084013e612a7e565b606091505b5091509150600082612a91576000612aa9565b818060200190516020811015612aa657600080fd5b50515b9050612abd886001600160a01b03166126d3565b8015612ac95750600081115b15612baa57604080516370a0823160e01b8152306004820152905173c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591632e1a7d4d9183916370a08231916024808301926020929190829003018186803b158015612b2757600080fd5b505afa158015612b3b573d6000803e3d6000fd5b505050506040513d6020811015612b5157600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015612b9157600080fd5b505af1158015612ba5573d6000803e3d6000fd5b505050505b98975050505050505050565b6000612bca826001600160a01b03166126d3565b15612bea5750734ddc2d193948926d02f9b1fe9e1daa0718270ed561270a565b6001600160a01b038216600080516020613e298339815191521415612c245750735d3a536e4d6dbd6114cc1ead35777bab948e364361270a565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415612c645750736c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e61270a565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e8621415612ca4575073158079ee67fce2f58472a96584a73c7ab9ac95c161270a565b6001600160a01b038216600080516020613e998339815191521415612cde57507339aa39c021dfbae8fac545936693ac917d5e756361270a565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415612d1e575073c11b1268c1a384e55c48c2391d8d480264a3a7f461270a565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415612d5e575073b3319f5d18bc0d84dd1b4825dcde5d5f7266d40761270a565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec71415612d9e575073f650c3d88d12db855b8bf7d11be6c55a4e07dcc961270a565b506000919050565b6000612db1836126d3565b15612dc757506001600160a01b03811631612632565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015612e1d57600080fd5b505afa158015612e31573d6000803e3d6000fd5b505050506040513d6020811015612e4757600080fd5b50519050612632565b6000612e64826001600160a01b03166126d3565b15612e845750733a3a65aab0dd2a17e3f1947ba16138cd37d08c0461270a565b6001600160a01b038216600080516020613e298339815191521415612ebe575073fc1e690f61efd961294b3e1ce3313fbd8aa4f85d61270a565b6001600160a01b038216600080516020613e998339815191521415612ef85750739ba00d6856a4edf4665bca2c2309936572473b7e61270a565b6001600160a01b0382167357ab1ec28d129707052df4df418d58a2d46d5f511415612f38575073625ae63000f46200499120b906716420bd05924061270a565b6001600160a01b038216734fabb145d64652a948d72533023f6e7a623c7c531415612f785750736ee0f7bb50a54ab5253da0667b0dc2ee526c30a861270a565b6001600160a01b0382166e085d4780b73119b644ae5ecd22b3761415612fb35750734da9b813057d04baef4e5800e36083717b4a034161270a565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec71415612ff357507371fc860f7d3a592a4a98740e39db31d25db65ae861270a565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415613033575073e1ba0fb44ccb0d11b80f92f4f8ed94ca3ff51d0061270a565b6001600160a01b03821673dd974d5c2e2928dea5f71b9825b8b646686bd20014156130735750739d91be44c06d373a8a226e1f3b146956083803eb61270a565b6001600160a01b0382167380fb784b7ed66730e8b1dbd9820afd29931aab0314156130b35750737d2d3688df45ce7c552e19c27e007673da9204b861270a565b6001600160a01b03821673514910771af9ca656af840dff83e8264ecf986ca14156130f3575073a64bd6c70cb9051f6a9ba1f163fdc07e0dfb5f8461270a565b6001600160a01b038216730f5d2fb29fb7d3cfee444a200298f468908cc94214156131335750736fce4a401b6b80ace52baaefe4421bd188e76f6f61270a565b6001600160a01b038216739f8f72aa9304c8b593d555f12ef6589cc3a579a214156131735750737deb5e830be29f91e298ba5ff1356bb7f814699861270a565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e86214156131b357507371010a9d003445ac60c4e6a7017c1e89a477b43861270a565b6001600160a01b03821673c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f14156131f3575073328c4c80bc7aca0834db37e6600a6c49e12da4de61270a565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415613233575073fc4b8ed459e00e5400be803a9bb3954234fd50e361270a565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415612d9e5750736fb0855c404e09c47c3fbca25f08d4e41f9f062f61270a565b600081836132ff5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156132c45781810151838201526020016132ac565b50505050905090810190601f1680156132f15780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161330b57fe5b0495945050505050565b61331e836126d3565b6133e6576000811180156133ac575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561337e57600080fd5b505afa158015613392573d6000803e3d6000fd5b505050506040513d60208110156133a857600080fd5b5051115b156133cc576133cc6001600160a01b03841683600063ffffffff613af516565b6133e66001600160a01b038416838363ffffffff613af516565b505050565b6060816001600160a01b0316836001600160a01b0316141561341c5750604080516000815260208101909152612632565b61342e836001600160a01b03166126d3565b1561344b5773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31592505b61345d826001600160a01b03166126d3565b1561347a5773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591505b6001600160a01b038316731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14806134c157506001600160a01b038216731f573d6fb3f13d689ff844b4ce37794d79a7ff1c145b156134ec5760408051600380825260808201909252906020820160608038833901905050905061350e565b60408051600580825260c08201909252906020820160a0803883390190505090505b6000806001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c146136d7576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b61356b6001600160a01b038b166126d3565b613575578961358b565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106136095780518252601f1990920191602091820191016135ea565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d806000811461366a576040519150601f19603f3d011682016040523d82523d6000602084013e61366f565b606091505b5091509150816136975760408051600080825260208201909252905b50945050505050612632565b8080602001905160208110156136ac57600080fd5b505193506001600160a01b0384166136d457604080516000808252602082019092529061368b565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14613895576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6137316001600160a01b038a166126d3565b61373b5788613751565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106137cf5780518252601f1990920191602091820191016137b0565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613830576040519150601f19603f3d011682016040523d82523d6000602084013e613835565b606091505b50915091508161385557604080516000808252602082019092529061368b565b80806020019051602081101561386a57600080fd5b505192506001600160a01b03831661389257604080516000808252602082019092529061368b565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156139585784836000815181106138c857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505081836001815181106138f657fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360028151811061393857fe5b6001600160a01b0390921660209283029190910190910152506126329050565b6001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156139fb57731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360008151811061399f57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505080836001815181106139cd57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050838360028151811061393857fe5b8483600081518110613a0957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508183600181518110613a3757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c83600281518110613a7957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508083600381518110613aa757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508383600481518110613ad557fe5b6001600160a01b0390921660209283029190910190910152505092915050565b801580613b7b575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b158015613b4d57600080fd5b505afa158015613b61573d6000803e3d6000fd5b505050506040513d6020811015613b7757600080fd5b5051155b613bb65760405162461bcd60e51b8152600401808060200182810382526036815260200180613ee36036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526133e6908490613c15826001600160a01b0316613dc1565b613c66576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310613ca45780518252601f199092019160209182019101613c85565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613d06576040519150601f19603f3d011682016040523d82523d6000602084013e613d0b565b606091505b509150915081613d62576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115613dbb57808060200190516020811015613d7e57600080fd5b5051613dbb5760405162461bcd60e51b815260040180806020018281038252602a815260200180613eb9602a913960400191505060405180910390fd5b50505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590613df557508115155b949350505050565b604051806101800160405280600c905b613e26815260200190600190039081613e0d5790505090565bfefe0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f73536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e63654f6e6553706c69743a20446973747269627574696f6e2061727261792073686f756c64206e6f74206578636565642072657365727665732061727261792073697a65a265627a7a72315820cb0d4a29fd87cd11889a615a9187b17763ab3bed25f43d40877f4c17748e037764736f6c63430005110032 \ No newline at end of file diff --git a/OneSplit.full.sol b/OneSplit.full.sol index 69da12c..2e4228a 100644 --- a/OneSplit.full.sol +++ b/OneSplit.full.sol @@ -5440,7 +5440,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase bntToken, amount, parts, - 0 + FLAG_DISABLE_SMART_TOKEN ); ( @@ -5451,7 +5451,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase toToken, returnBntAmount, parts, - 0 + FLAG_DISABLE_SMART_TOKEN ); for (uint i = 0; i < smartTokenToDistribution.length; i++) { @@ -5467,7 +5467,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase toToken, amount, parts, - 0 + FLAG_DISABLE_SMART_TOKEN ); } @@ -5477,7 +5477,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase toToken, amount, parts, - 0 + FLAG_DISABLE_SMART_TOKEN ); } } @@ -5522,7 +5522,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase continue; } - (uint256 ret, uint256[] memory dist) = getExpectedReturn( + (uint256 ret, uint256[] memory dist) = this.getExpectedReturn( _canonicalSUSD(details.tokens[i].token), toToken, srcAmount, @@ -5568,7 +5568,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase .div(details.totalRatio); if (details.tokens[i].token != fromToken) { - (tokenAmounts[i], dist) = getExpectedReturn( + (tokenAmounts[i], dist) = this.getExpectedReturn( fromToken, _canonicalSUSD(details.tokens[i].token), exchangeAmount, @@ -5636,7 +5636,6 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { uint256[] memory distribution, uint256 flags ) internal { - if (fromToken == toToken) { return; } @@ -5659,7 +5658,7 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { bntToken, amount, dist, - 0 + FLAG_DISABLE_SMART_TOKEN ); for (uint i = 0; i < distribution.length; i++) { @@ -5673,7 +5672,7 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { toToken, bntBalanceAfter.sub(bntBalanceBefore), dist, - 0 + FLAG_DISABLE_SMART_TOKEN ); } @@ -5683,7 +5682,7 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { toToken, amount, distribution, - 0 + FLAG_DISABLE_SMART_TOKEN ); } @@ -5693,7 +5692,7 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { toToken, amount, distribution, - 0 + FLAG_DISABLE_SMART_TOKEN ); } } @@ -5767,10 +5766,11 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { dist[j] = (distribution[j] >> (i * 8)) & 0xFF; } - super._swap( + this.swap( fromToken, _canonicalSUSD(details.tokens[i].token), exchangeAmount, + 0, dist, flags ); @@ -5851,7 +5851,7 @@ contract OneSplitViewWrap is OneSplitIearnView, //OneSplitIdleView, OneSplitWethView, - OneSplitBalancerPoolTokenView, + //OneSplitBalancerPoolTokenView, OneSplitUniswapPoolTokenView, OneSplitCurvePoolTokenView, OneSplitSmartTokenView diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 91a9599..c693145 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -107,7 +107,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase bntToken, amount, parts, - 0 + FLAG_DISABLE_SMART_TOKEN ); ( @@ -118,7 +118,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase toToken, returnBntAmount, parts, - 0 + FLAG_DISABLE_SMART_TOKEN ); for (uint i = 0; i < smartTokenToDistribution.length; i++) { @@ -134,7 +134,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase toToken, amount, parts, - 0 + FLAG_DISABLE_SMART_TOKEN ); } @@ -144,7 +144,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase toToken, amount, parts, - 0 + FLAG_DISABLE_SMART_TOKEN ); } } @@ -189,7 +189,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase continue; } - (uint256 ret, uint256[] memory dist) = getExpectedReturn( + (uint256 ret, uint256[] memory dist) = this.getExpectedReturn( _canonicalSUSD(details.tokens[i].token), toToken, srcAmount, @@ -235,7 +235,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase .div(details.totalRatio); if (details.tokens[i].token != fromToken) { - (tokenAmounts[i], dist) = getExpectedReturn( + (tokenAmounts[i], dist) = this.getExpectedReturn( fromToken, _canonicalSUSD(details.tokens[i].token), exchangeAmount, @@ -303,7 +303,6 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { uint256[] memory distribution, uint256 flags ) internal { - if (fromToken == toToken) { return; } @@ -326,7 +325,7 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { bntToken, amount, dist, - 0 + FLAG_DISABLE_SMART_TOKEN ); for (uint i = 0; i < distribution.length; i++) { @@ -340,7 +339,7 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { toToken, bntBalanceAfter.sub(bntBalanceBefore), dist, - 0 + FLAG_DISABLE_SMART_TOKEN ); } @@ -350,7 +349,7 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { toToken, amount, distribution, - 0 + FLAG_DISABLE_SMART_TOKEN ); } @@ -360,7 +359,7 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { toToken, amount, distribution, - 0 + FLAG_DISABLE_SMART_TOKEN ); } } @@ -434,10 +433,11 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { dist[j] = (distribution[j] >> (i * 8)) & 0xFF; } - super._swap( + this.swap( fromToken, _canonicalSUSD(details.tokens[i].token), exchangeAmount, + 0, dist, flags ); From cfbd4c98a2c0ff62dea73cfa5199bc7a688563d7 Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 8 May 2020 01:10:25 +0300 Subject: [PATCH 53/60] uniswap v2 pool token --- OneSplit.full.abi | 2 +- OneSplit.full.bin | 2 +- OneSplit.full.sol | 475 ++++++++++++++++++++++- contracts/IOneSplit.sol | 1 + contracts/OneSplit.sol | 7 +- contracts/OneSplitUniswapPoolToken.sol | 2 +- contracts/OneSplitUniswapV2PoolToken.sol | 395 +++++++++++++++++++ contracts/interface/IUniswapV2Pair.sol | 15 + contracts/interface/IUniswapV2Pool.sol | 21 + 9 files changed, 898 insertions(+), 22 deletions(-) create mode 100644 contracts/OneSplitUniswapV2PoolToken.sol create mode 100644 contracts/interface/IUniswapV2Pair.sol create mode 100644 contracts/interface/IUniswapV2Pool.sol diff --git a/OneSplit.full.abi b/OneSplit.full.abi index 923bcab..ca06130 100644 --- a/OneSplit.full.abi +++ b/OneSplit.full.abi @@ -1 +1 @@ -[{"inputs":[{"internalType":"contract IOneSplitView","name":"_oneSplitView","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[],"name":"DEXES_COUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ETH_ADDRESS","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BALANCER_POOL_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BANCOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BDAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_BINANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_SYNTHETIX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_USDT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_Y","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_ZAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_FULCRUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IDLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IEARN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_KYBER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_OASIS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_SMART_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_POOL_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_WETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_BANCOR_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_OASIS_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_UNISWAP_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_DAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_USDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"aave","outputs":[{"internalType":"contract IAaveLendingPool","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorContractRegistry","outputs":[{"internalType":"contract IBancorContractRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorConverterRegistry","outputs":[{"internalType":"contract IBancorConverterRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorEtherToken","outputs":[{"internalType":"contract IBancorEtherToken","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bnt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"busd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cETH","outputs":[{"internalType":"contract ICompoundEther","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"chai","outputs":[{"internalType":"contract IChai","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"compound","outputs":[{"internalType":"contract ICompound","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveBinance","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveCompound","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveSynthetix","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveUsdt","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveY","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"}],"name":"getExpectedReturn","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"kyberNetworkProxy","outputs":[{"internalType":"contract IKyberNetworkProxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oasisExchange","outputs":[{"internalType":"contract IOasisExchange","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oneSplitView","outputs":[{"internalType":"contract IOneSplitView","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"susd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"swap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"tusd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"uniswapFactory","outputs":[{"internalType":"contract IUniswapFactory","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdc","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"wethToken","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file +[{"inputs":[{"internalType":"contract IOneSplitView","name":"_oneSplitView","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[],"name":"DEXES_COUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ETH_ADDRESS","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BALANCER_POOL_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BANCOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BDAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_BINANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_SYNTHETIX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_USDT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_Y","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_ZAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_FULCRUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IDLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IEARN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_KYBER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_OASIS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_SMART_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_POOL_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_WETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_BANCOR_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_OASIS_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_UNISWAP_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_DAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_USDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"aave","outputs":[{"internalType":"contract IAaveLendingPool","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorContractRegistry","outputs":[{"internalType":"contract IBancorContractRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorConverterRegistry","outputs":[{"internalType":"contract IBancorConverterRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorEtherToken","outputs":[{"internalType":"contract IBancorEtherToken","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bnt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"busd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cETH","outputs":[{"internalType":"contract ICompoundEther","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"chai","outputs":[{"internalType":"contract IChai","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"compound","outputs":[{"internalType":"contract ICompound","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveBinance","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveCompound","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveSynthetix","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveUsdt","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveY","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"}],"name":"getExpectedReturn","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"kyberNetworkProxy","outputs":[{"internalType":"contract IKyberNetworkProxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oasisExchange","outputs":[{"internalType":"contract IOasisExchange","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oneSplitView","outputs":[{"internalType":"contract IOneSplitView","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"susd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"swap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"tusd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"uniswapFactory","outputs":[{"internalType":"contract IUniswapFactory","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdc","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"wethToken","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/OneSplit.full.bin b/OneSplit.full.bin index 0d456a3..b570d64 100644 --- a/OneSplit.full.bin +++ b/OneSplit.full.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b50604051613ff4380380613ff48339818101604052602081101561003357600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055613f8f806100656000396000f3fe6080604052600436106103815760003560e01c806375b5be2d116101d1578063c925777511610102578063d70a2d1f116100a0578063f4b9fa751161006f578063f4b9fa7514610976578063f56e281f1461098b578063f69e2046146109a0578063fbe4ed95146109b557610381565b8063d70a2d1f1461086d578063d77366a414610882578063dc1536b214610897578063e2a7515e146108ac57610381565b8063cc26e9fc116100dc578063cc26e9fc14610819578063cede5f6a1461082e578063d1aee5e314610843578063d393c3e91461085857610381565b8063c9257775146107da578063c989b667146107ef578063c9b42c671461080457610381565b8063a1b4d0111161016f578063b3bc784411610149578063b3bc784414610786578063b69d04561461079b578063c762a46c146107b0578063c77b9de6146107c557610381565b8063a1b4d01114610747578063a734f06e1461075c578063b0a7ef291461077157610381565b8063819faf7b116101ab578063819faf7b146106f3578063851954fa146107085780638aea49d21461071d5780638bdb2afa1461073257610381565b806375b5be2d146106b45780637a88bdbd146106c95780637e09b9c2146106de57610381565b80633e413bee116102b657806351f1985c1161025457806364ec4e5c1161022357806364ec4e5c1461066057806368e2a014146106755780636cbc4a6e1461068a57806375a8b0121461069f57610381565b806351f1985c1461060c5780635aa8fb48146106215780635ae51b82146106365780635c0cb4791461064b57610381565b8063423d03f911610290578063423d03f9146105b857806344211d62146105cd5780634a7101d5146105e25780634b57b0be146105f757610381565b80633e413bee1461057957806340ab7b8c1461058e5780634226a9b9146105a357610381565b806322320c98116103235780632f48ab7d116102fd5780632f48ab7d1461052557806334b4dabb1461053a578063372a26cb1461054f5780633ca5b2341461056457610381565b806322320c98146104e65780632d3b5207146104fb5780632e707bd21461051057610381565b80631388b4201161035f5780631388b4201461049257806313989140146104a75780632113240d146104bc57806321a360f5146104d157610381565b806305d8aa0a14610390578063085e2c5b146103b757806312dea16014610461575b3332141561038e57600080fd5b005b34801561039c57600080fd5b506103a56109ca565b60408051918252519081900360200190f35b3480156103c357600080fd5b50610406600480360360a08110156103da57600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608001356109d1565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b8381101561044c578181015183820152602001610434565b50505050905001935050505060405180910390f35b34801561046d57600080fd5b50610476610b1d565b604080516001600160a01b039092168252519081900360200190f35b34801561049e57600080fd5b50610476610b35565b3480156104b357600080fd5b506103a5610b4d565b3480156104c857600080fd5b506103a5610b53565b3480156104dd57600080fd5b506103a5610b59565b3480156104f257600080fd5b50610476610b62565b34801561050757600080fd5b506103a5610b7a565b34801561051c57600080fd5b506103a5610b83565b34801561053157600080fd5b50610476610b88565b34801561054657600080fd5b506103a5610ba0565b34801561055b57600080fd5b50610476610ba5565b34801561057057600080fd5b50610476610bbd565b34801561058557600080fd5b50610476610bd5565b34801561059a57600080fd5b50610476610be7565b3480156105af57600080fd5b506103a5610bff565b3480156105c457600080fd5b50610476610c07565b3480156105d957600080fd5b506103a5610c1f565b3480156105ee57600080fd5b506103a5610c24565b34801561060357600080fd5b50610476610c29565b34801561061857600080fd5b50610476610c41565b34801561062d57600080fd5b506103a5610c59565b34801561064257600080fd5b506103a5610c5f565b34801561065757600080fd5b506103a5610c65565b34801561066c57600080fd5b506103a5610c6a565b34801561068157600080fd5b506103a5610c71565b34801561069657600080fd5b506103a5610c78565b3480156106ab57600080fd5b506103a5610c7f565b3480156106c057600080fd5b50610476610c85565b3480156106d557600080fd5b506103a5610c98565b3480156106ea57600080fd5b506103a5610c9d565b3480156106ff57600080fd5b50610476610ca4565b34801561071457600080fd5b50610476610cbc565b34801561072957600080fd5b506103a5610cd4565b34801561073e57600080fd5b50610476610cdc565b34801561075357600080fd5b50610476610cf4565b34801561076857600080fd5b50610476610d0c565b34801561077d57600080fd5b506103a5610d24565b34801561079257600080fd5b506103a5610d2a565b3480156107a757600080fd5b50610476610d33565b3480156107bc57600080fd5b506103a5610d4b565b3480156107d157600080fd5b506103a5610d50565b3480156107e657600080fd5b50610476610d56565b3480156107fb57600080fd5b506103a5610d6e565b34801561081057600080fd5b506103a5610d75565b34801561082557600080fd5b506103a5610d7c565b34801561083a57600080fd5b50610476610d81565b34801561084f57600080fd5b506103a5610d99565b34801561086457600080fd5b506103a5610da1565b34801561087957600080fd5b50610476610da8565b34801561088e57600080fd5b50610476610dc0565b3480156108a357600080fd5b506103a5610dd8565b61038e600480360360c08110156108c257600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a08101608082013564010000000081111561090257600080fd5b82018360208201111561091457600080fd5b8035906020019184602083028401116401000000008311171561093657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610dde915050565b34801561098257600080fd5b50610476611000565b34801561099757600080fd5b506103a5611012565b3480156109ac57600080fd5b50610476611017565b3480156109c157600080fd5b5061047661102f565b6220000081565b600080546040805163085e2c5b60e01b81526001600160a01b03898116600483015288811660248301526044820188905260648201879052608482018690529151606093929092169163085e2c5b9160a4808201928792909190829003018186803b158015610a3f57600080fd5b505afa158015610a53573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610a7c57600080fd5b815160208301805160405192949293830192919084640100000000821115610aa357600080fd5b908301906020820185811115610ab857600080fd5b8251866020820283011164010000000082111715610ad557600080fd5b82525081516020918201928201910280838360005b83811015610b02578181015183820152602001610aea565b50505050905001604052505050915091509550959350505050565b7352ae12abe5d8bd778bd5397f99ca900624cfadd481565b73794e6e91555438afc3ccf1c5076a74f42133d08d81565b61200081565b61800081565b64020000000081565b73a5407eae9ba41422680e2e00537571bcc53efbfd81565b64010000000081565b608081565b73dac17f958d2ee523a2206206994597c13d831ec781565b604081565b7379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2781565b734fabb145d64652a948d72533023f6e7a623c7c5381565b600080516020613e9983398151915281565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c81565b630100000081565b7345f783cce6b7ff23b2ab2d70e416cdb7d6055f5181565b601081565b602081565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b73a2b47e3d5c44877cca798226b7b8118f9bfb7a5681565b61400081565b61080081565b600881565b6202000081565b6210000081565b6208000081565b61040081565b6e085d4780b73119b644ae5ecd22b37681565b600281565b6240000081565b73398ec7346dcd622edc5ae82352f02be94c62d11981565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce31581565b630400000081565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9581565b734ddc2d193948926d02f9b1fe9e1daa0718270ed581565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b7306af07097c9eeb7fd685c692751d5c66db49c21581565b600181565b61020081565b7357ab1ec28d129707052df4df418d58a2d46d5f5181565b6280000081565b6204000081565b600c81565b7352ea46506b9cc5ef470c5bf89f17dc28bb35d85c81565b630200000081565b6201000081565b73f6e2d7f616b67e46d708e4410746e9aab3a4c51881565b73818e6fecd516ecc3849daf6845e3ec868087b75581565b61010081565b846001600160a01b0316866001600160a01b03161415610dfd57610ff8565b610e05613dfd565b60405180610180016040528061103e81526020016112bf815260200161144a8152602001611467815260200161174081526020016118cb8152602001611a9c8152602001611cc18152602001611ef0815260200161211f81526020016122bd81526020016124698152509050600c83511115610eb25760405162461bcd60e51b8152600401808060200182810382526042815260200180613f196042913960600191505060405180910390fd5b600080805b8551811015610f10576000868281518110610ece57fe5b60200260200101511115610f0857610f02868281518110610eeb57fe5b6020026020010151846125d590919063ffffffff16565b92508091505b600101610eb7565b5060008211610f505760405162461bcd60e51b815260040180806020018281038252602f815260200180613e49602f913960400191505060405180910390fd5b8660005b8651811015610ff257868181518110610f6957fe5b602002602001015160001415610f7e57610fea565b6000610fb685610faa8a8581518110610f9357fe5b60200260200101518d61263890919063ffffffff16565b9063ffffffff61269116565b905083821415610fc35750815b8083039250610fe78c8c838986600c8110610fda57fe5b602002015163ffffffff16565b50505b600101610f54565b50505050505b505050505050565b600080516020613e2983398151915281565b600481565b733d9819210a31b4961b30ef54be2aed79b9c9cd3b81565b6000546001600160a01b031681565b6000816110536001600160a01b0386166126d3565b61118357604080516303795fb160e11b81526001600160a01b0387166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156110b557600080fd5b505afa1580156110c9573d6000803e3d6000fd5b505050506040513d60208110156110df57600080fd5b505190506001600160a01b03811615611181576110fc868261270f565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561115257600080fd5b505af1158015611166573d6000803e3d6000fd5b505050506040513d602081101561117c57600080fd5b505191505b505b611195846001600160a01b03166126d3565b6112b557604080516303795fb160e11b81526001600160a01b0386166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156111f757600080fd5b505afa15801561120b573d6000803e3d6000fd5b505050506040513d602081101561122157600080fd5b505190506001600160a01b038116156112b357806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b15801561128357600080fd5b505af1158015611297573d6000803e3d6000fd5b50505050506040513d60208110156112ae57600080fd5b505191505b505b90505b9392505050565b60006112df8473818e6fecd516ecc3849daf6845e3ec868087b75561270f565b73818e6fecd516ecc3849daf6845e3ec868087b7556329589f6161130b6001600160a01b0387166126d3565b611316576000611318565b835b61132a876001600160a01b03166126d3565b611334578661134a565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b8561135d886001600160a01b03166126d3565b611367578761137d565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b15801561141557600080fd5b505af1158015611429573d6000803e3d6000fd5b50505050506040513d602081101561144057600080fd5b5051949350505050565b6000806114588585856127c8565b9050600081116112b557600080fd5b600061147b846001600160a01b03166126d3565b156114e95773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156114cf57600080fd5b505af11580156114e3573d6000803e3d6000fd5b50505050505b6115386114fe856001600160a01b03166126d3565b611508578461151e565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b73794e6e91555438afc3ccf1c5076a74f42133d08d61270f565b600073794e6e91555438afc3ccf1c5076a74f42133d08d630621b4f66115666001600160a01b0388166126d3565b6115705786611586565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b85611599886001600160a01b03166126d3565b6115a357876115b9565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b15801561161757600080fd5b505af115801561162b573d6000803e3d6000fd5b505050506040513d602081101561164157600080fd5b505190506116576001600160a01b0385166126d3565b156112b557604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b1580156116b557600080fd5b505afa1580156116c9573d6000803e3d6000fd5b505050506040513d60208110156116df57600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561171f57600080fd5b505af1158015611733573d6000803e3d6000fd5b5050505090509392505050565b6000806001600160a01b038516600080516020613e998339815191521461176857600061176b565b60025b6001600160a01b038616600080516020613e2983398151915214611790576000611793565b60015b0160ff1690506000600080516020613e998339815191526001600160a01b038616146117c05760006117c3565b60025b6001600160a01b038616600080516020613e29833981519152146117e85760006117eb565b60015b0160ff16905081600f0b60001480611806575080600f0b6000145b15611816576000925050506112b8565b6118348673a2b47e3d5c44877cca798226b7b8118f9bfb7a5661270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a2b47e3d5c44877cca798226b7b8118f9bfb7a569263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b505af11580156118be573d6000803e3d6000fd5b5050505050509392505050565b6000806001600160a01b03851673dac17f958d2ee523a2206206994597c13d831ec7146118f95760006118fc565b60035b6001600160a01b038616600080516020613e9983398151915214611921576000611924565b60025b6001600160a01b038716600080516020613e298339815191521461194957600061194c565b60015b010160ff169050600073dac17f958d2ee523a2206206994597c13d831ec76001600160a01b0316856001600160a01b03161461198957600061198c565b60035b6001600160a01b038616600080516020613e99833981519152146119b15760006119b4565b60025b6001600160a01b038716600080516020613e29833981519152146119d95760006119dc565b60015b010160ff16905081600f0b600014806119f8575080600f0b6000145b15611a08576000925050506112b8565b611a26867352ea46506b9cc5ef470c5bf89f17dc28bb35d85c61270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517352ea46506b9cc5ef470c5bf89f17dc28bb35d85c9263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000806001600160a01b0385166e085d4780b73119b644ae5ecd22b37614611ac5576000611ac8565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611af3576000611af6565b60035b6001600160a01b038716600080516020613e9983398151915214611b1b576000611b1e565b60025b6001600160a01b038816600080516020613e2983398151915214611b43576000611b46565b60015b01010160ff16905060006e085d4780b73119b644ae5ecd22b3766001600160a01b0316856001600160a01b031614611b7f576000611b82565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611bad576000611bb0565b60035b6001600160a01b038716600080516020613e9983398151915214611bd5576000611bd8565b60025b6001600160a01b038816600080516020613e2983398151915214611bfd576000611c00565b60015b01010160ff16905081600f0b60001480611c1d575080600f0b6000145b15611c2d576000925050506112b8565b611c4b867345f783cce6b7ff23b2ab2d70e416cdb7d6055f5161270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517345f783cce6b7ff23b2ab2d70e416cdb7d6055f519263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000806001600160a01b038516734fabb145d64652a948d72533023f6e7a623c7c5314611cef576000611cf2565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611d1d576000611d20565b60035b6001600160a01b038716600080516020613e9983398151915214611d45576000611d48565b60025b6001600160a01b038816600080516020613e2983398151915214611d6d576000611d70565b60015b01010160ff1690506000734fabb145d64652a948d72533023f6e7a623c7c536001600160a01b0316856001600160a01b031614611dae576000611db1565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611ddc576000611ddf565b60035b6001600160a01b038716600080516020613e9983398151915214611e04576000611e07565b60025b6001600160a01b038816600080516020613e2983398151915214611e2c576000611e2f565b60015b01010160ff16905081600f0b60001480611e4c575080600f0b6000145b15611e5c576000925050506112b8565b611e7a867379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2761270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517379a8c46dea5ada233abaffd40f3a0a2b1e5a4f279263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000806001600160a01b0385167357ab1ec28d129707052df4df418d58a2d46d5f5114611f1e576000611f21565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611f4c576000611f4f565b60035b6001600160a01b038716600080516020613e9983398151915214611f74576000611f77565b60025b6001600160a01b038816600080516020613e2983398151915214611f9c576000611f9f565b60015b01010160ff16905060007357ab1ec28d129707052df4df418d58a2d46d5f516001600160a01b0316856001600160a01b031614611fdd576000611fe0565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec71461200b57600061200e565b60035b6001600160a01b038716600080516020613e9983398151915214612033576000612036565b60025b6001600160a01b038816600080516020613e298339815191521461205b57600061205e565b60015b01010160ff16905081600f0b6000148061207b575080600f0b6000145b1561208b576000925050506112b8565b6120a98673a5407eae9ba41422680e2e00537571bcc53efbfd61270f565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a5407eae9ba41422680e2e00537571bcc53efbfd9263a6417ed6926084808201939182900301818387803b1580156118aa57600080fd5b6000612133846001600160a01b03166126d3565b6121ec57600061214285612bb6565b905061214e858261270f565b806001600160a01b031663a0712d68846040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561219457600080fd5b505af11580156121a8573d6000803e3d6000fd5b505050506040513d60208110156121be57600080fd5b506121e4905081856121df6001600160a01b0383163063ffffffff612da616565b61103e565b9150506112b8565b6121fe836001600160a01b03166126d3565b6122b357600061220d84612bb6565b9050600061221c86838661103e565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561226457600080fd5b505af1158015612278573d6000803e3d6000fd5b505050506040513d602081101561228e57600080fd5b506122aa90506001600160a01b0386163063ffffffff612da616565b925050506112b8565b5060009392505050565b60006001600160a01b038416600080516020613e2983398151915214156123a0576122fc847306af07097c9eeb7fd685c692751d5c66db49c21561270f565b60408051633b4da69f60e01b81523060048201526024810184905290517306af07097c9eeb7fd685c692751d5c66db49c21591633b4da69f91604480830192600092919082900301818387803b15801561235557600080fd5b505af1158015612369573d6000803e3d6000fd5b5061239992507306af07097c9eeb7fd685c692751d5c66db49c21591508590506121df823063ffffffff612da616565b90506112b8565b6001600160a01b038316600080516020613e2983398151915214156122b35760006123e0857306af07097c9eeb7fd685c692751d5c66db49c2158561103e565b6040805163ef693bed60e01b81523060048201526024810183905290519192507306af07097c9eeb7fd685c692751d5c66db49c2159163ef693bed9160448082019260009290919082900301818387803b15801561243d57600080fd5b505af1158015612451573d6000803e3d6000fd5b506121e4925050506001600160a01b03851630612da6565b600061247d846001600160a01b03166126d3565b61253b57600061248c85612e50565b9050612498858261270f565b60408051636968703360e11b81526001600160a01b03871660048201526024810185905261044d6044820152905173398ec7346dcd622edc5ae82352f02be94c62d1199163d2d0e06691606480830192600092919082900301818387803b15801561250257600080fd5b505af1158015612516573d6000803e3d6000fd5b505050506121e481856121df30856001600160a01b0316612da690919063ffffffff16565b61254d836001600160a01b03166126d3565b6122b357600061255c84612e50565b9050600061256b86838661103e565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156125b357600080fd5b505af11580156125c7573d6000803e3d6000fd5b5050505080925050506112b8565b60008282018381101561262f576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60008261264757506000612632565b8282028284828161265457fe5b041461262f5760405162461bcd60e51b8152600401808060200182810382526021815260200180613e786021913960400191505060405180910390fd5b600061262f83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250613273565b60006001600160a01b038216158061270757506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b90505b919050565b612721826001600160a01b03166126d3565b6127c45760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b15801561277657600080fd5b505afa15801561278a573d6000803e3d6000fd5b505050506040513d60208110156127a057600080fd5b5051901c6127c4576127c46001600160a01b0383168260001963ffffffff61331516565b5050565b60006127dc846001600160a01b03166126d3565b1561284a5773c0829421c1d260bd3cb3e0f06cfe2d52db2ce3156001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561283057600080fd5b505af1158015612844573d6000803e3d6000fd5b50505050505b60007352ae12abe5d8bd778bd5397f99ca900624cfadd46001600160a01b031663bb34534c6040518163ffffffff1660e01b815260040180806c42616e636f724e6574776f726b60981b815250602001905060206040518083038186803b1580156128b457600080fd5b505afa1580156128c8573d6000803e3d6000fd5b505050506040513d60208110156128de57600080fd5b5051905060606128ee86866133eb565b905061292b612905876001600160a01b03166126d3565b61290f5786612925565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b8361270f565b60006060836001600160a01b03166216e360856001600160a01b031663c7ba24bc905060e01b8589600160405160240180806020018481526020018360ff168152602001828103825285818151815260200191508051906020019060200280838360005b838110156129a757818101518382015260200161298f565b50505050905001945050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0383818316178352505050506040518082805190602001908083835b60208310612a165780518252601f1990920191602091820191016129f7565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038160008787f1925050503d8060008114612a79576040519150601f19603f3d011682016040523d82523d6000602084013e612a7e565b606091505b5091509150600082612a91576000612aa9565b818060200190516020811015612aa657600080fd5b50515b9050612abd886001600160a01b03166126d3565b8015612ac95750600081115b15612baa57604080516370a0823160e01b8152306004820152905173c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591632e1a7d4d9183916370a08231916024808301926020929190829003018186803b158015612b2757600080fd5b505afa158015612b3b573d6000803e3d6000fd5b505050506040513d6020811015612b5157600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015612b9157600080fd5b505af1158015612ba5573d6000803e3d6000fd5b505050505b98975050505050505050565b6000612bca826001600160a01b03166126d3565b15612bea5750734ddc2d193948926d02f9b1fe9e1daa0718270ed561270a565b6001600160a01b038216600080516020613e298339815191521415612c245750735d3a536e4d6dbd6114cc1ead35777bab948e364361270a565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415612c645750736c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e61270a565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e8621415612ca4575073158079ee67fce2f58472a96584a73c7ab9ac95c161270a565b6001600160a01b038216600080516020613e998339815191521415612cde57507339aa39c021dfbae8fac545936693ac917d5e756361270a565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415612d1e575073c11b1268c1a384e55c48c2391d8d480264a3a7f461270a565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415612d5e575073b3319f5d18bc0d84dd1b4825dcde5d5f7266d40761270a565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec71415612d9e575073f650c3d88d12db855b8bf7d11be6c55a4e07dcc961270a565b506000919050565b6000612db1836126d3565b15612dc757506001600160a01b03811631612632565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015612e1d57600080fd5b505afa158015612e31573d6000803e3d6000fd5b505050506040513d6020811015612e4757600080fd5b50519050612632565b6000612e64826001600160a01b03166126d3565b15612e845750733a3a65aab0dd2a17e3f1947ba16138cd37d08c0461270a565b6001600160a01b038216600080516020613e298339815191521415612ebe575073fc1e690f61efd961294b3e1ce3313fbd8aa4f85d61270a565b6001600160a01b038216600080516020613e998339815191521415612ef85750739ba00d6856a4edf4665bca2c2309936572473b7e61270a565b6001600160a01b0382167357ab1ec28d129707052df4df418d58a2d46d5f511415612f38575073625ae63000f46200499120b906716420bd05924061270a565b6001600160a01b038216734fabb145d64652a948d72533023f6e7a623c7c531415612f785750736ee0f7bb50a54ab5253da0667b0dc2ee526c30a861270a565b6001600160a01b0382166e085d4780b73119b644ae5ecd22b3761415612fb35750734da9b813057d04baef4e5800e36083717b4a034161270a565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec71415612ff357507371fc860f7d3a592a4a98740e39db31d25db65ae861270a565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415613033575073e1ba0fb44ccb0d11b80f92f4f8ed94ca3ff51d0061270a565b6001600160a01b03821673dd974d5c2e2928dea5f71b9825b8b646686bd20014156130735750739d91be44c06d373a8a226e1f3b146956083803eb61270a565b6001600160a01b0382167380fb784b7ed66730e8b1dbd9820afd29931aab0314156130b35750737d2d3688df45ce7c552e19c27e007673da9204b861270a565b6001600160a01b03821673514910771af9ca656af840dff83e8264ecf986ca14156130f3575073a64bd6c70cb9051f6a9ba1f163fdc07e0dfb5f8461270a565b6001600160a01b038216730f5d2fb29fb7d3cfee444a200298f468908cc94214156131335750736fce4a401b6b80ace52baaefe4421bd188e76f6f61270a565b6001600160a01b038216739f8f72aa9304c8b593d555f12ef6589cc3a579a214156131735750737deb5e830be29f91e298ba5ff1356bb7f814699861270a565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e86214156131b357507371010a9d003445ac60c4e6a7017c1e89a477b43861270a565b6001600160a01b03821673c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f14156131f3575073328c4c80bc7aca0834db37e6600a6c49e12da4de61270a565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415613233575073fc4b8ed459e00e5400be803a9bb3954234fd50e361270a565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415612d9e5750736fb0855c404e09c47c3fbca25f08d4e41f9f062f61270a565b600081836132ff5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156132c45781810151838201526020016132ac565b50505050905090810190601f1680156132f15780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161330b57fe5b0495945050505050565b61331e836126d3565b6133e6576000811180156133ac575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561337e57600080fd5b505afa158015613392573d6000803e3d6000fd5b505050506040513d60208110156133a857600080fd5b5051115b156133cc576133cc6001600160a01b03841683600063ffffffff613af516565b6133e66001600160a01b038416838363ffffffff613af516565b505050565b6060816001600160a01b0316836001600160a01b0316141561341c5750604080516000815260208101909152612632565b61342e836001600160a01b03166126d3565b1561344b5773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31592505b61345d826001600160a01b03166126d3565b1561347a5773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591505b6001600160a01b038316731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14806134c157506001600160a01b038216731f573d6fb3f13d689ff844b4ce37794d79a7ff1c145b156134ec5760408051600380825260808201909252906020820160608038833901905050905061350e565b60408051600580825260c08201909252906020820160a0803883390190505090505b6000806001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c146136d7576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b61356b6001600160a01b038b166126d3565b613575578961358b565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106136095780518252601f1990920191602091820191016135ea565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d806000811461366a576040519150601f19603f3d011682016040523d82523d6000602084013e61366f565b606091505b5091509150816136975760408051600080825260208201909252905b50945050505050612632565b8080602001905160208110156136ac57600080fd5b505193506001600160a01b0384166136d457604080516000808252602082019092529061368b565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14613895576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6137316001600160a01b038a166126d3565b61373b5788613751565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106137cf5780518252601f1990920191602091820191016137b0565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613830576040519150601f19603f3d011682016040523d82523d6000602084013e613835565b606091505b50915091508161385557604080516000808252602082019092529061368b565b80806020019051602081101561386a57600080fd5b505192506001600160a01b03831661389257604080516000808252602082019092529061368b565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156139585784836000815181106138c857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505081836001815181106138f657fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360028151811061393857fe5b6001600160a01b0390921660209283029190910190910152506126329050565b6001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156139fb57731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360008151811061399f57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505080836001815181106139cd57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050838360028151811061393857fe5b8483600081518110613a0957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508183600181518110613a3757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c83600281518110613a7957fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508083600381518110613aa757fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508383600481518110613ad557fe5b6001600160a01b0390921660209283029190910190910152505092915050565b801580613b7b575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b158015613b4d57600080fd5b505afa158015613b61573d6000803e3d6000fd5b505050506040513d6020811015613b7757600080fd5b5051155b613bb65760405162461bcd60e51b8152600401808060200182810382526036815260200180613ee36036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526133e6908490613c15826001600160a01b0316613dc1565b613c66576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310613ca45780518252601f199092019160209182019101613c85565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613d06576040519150601f19603f3d011682016040523d82523d6000602084013e613d0b565b606091505b509150915081613d62576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115613dbb57808060200190516020811015613d7e57600080fd5b5051613dbb5760405162461bcd60e51b815260040180806020018281038252602a815260200180613eb9602a913960400191505060405180910390fd5b50505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590613df557508115155b949350505050565b604051806101800160405280600c905b613e26815260200190600190039081613e0d5790505090565bfefe0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f73536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e63654f6e6553706c69743a20446973747269627574696f6e2061727261792073686f756c64206e6f74206578636565642072657365727665732061727261792073697a65a265627a7a72315820cb0d4a29fd87cd11889a615a9187b17763ab3bed25f43d40877f4c17748e037764736f6c63430005110032 \ No newline at end of file +608060405234801561001057600080fd5b5060405161401c38038061401c8339818101604052602081101561003357600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055613fb7806100656000396000f3fe60806040526004361061038c5760003560e01c806375b5be2d116101dc578063c925777511610102578063d70a2d1f116100a0578063f4b9fa751161006f578063f4b9fa7514610996578063f56e281f146109ab578063f69e2046146109c0578063fbe4ed95146109d55761038c565b8063d70a2d1f1461088d578063d77366a4146108a2578063dc1536b2146108b7578063e2a7515e146108cc5761038c565b8063cc26e9fc116100dc578063cc26e9fc14610839578063cede5f6a1461084e578063d1aee5e314610863578063d393c3e9146108785761038c565b8063c9257775146107fa578063c989b6671461080f578063c9b42c67146108245761038c565b80638ea812c01161017a578063b3bc784411610149578063b3bc7844146107a6578063b69d0456146107bb578063c762a46c146107d0578063c77b9de6146107e55761038c565b80638ea812c014610752578063a1b4d01114610767578063a734f06e1461077c578063b0a7ef29146107915761038c565b8063819faf7b116101b6578063819faf7b146106fe578063851954fa146107135780638aea49d2146107285780638bdb2afa1461073d5761038c565b806375b5be2d146106bf5780637a88bdbd146106d45780637e09b9c2146106e95761038c565b80633e413bee116102c157806351f1985c1161025f57806364ec4e5c1161022e57806364ec4e5c1461066b57806368e2a014146106805780636cbc4a6e1461069557806375a8b012146106aa5761038c565b806351f1985c146106175780635aa8fb481461062c5780635ae51b82146106415780635c0cb479146106565761038c565b8063423d03f91161029b578063423d03f9146105c357806344211d62146105d85780634a7101d5146105ed5780634b57b0be146106025761038c565b80633e413bee1461058457806340ab7b8c146105995780634226a9b9146105ae5761038c565b806322320c981161032e5780632f48ab7d116103085780632f48ab7d1461053057806334b4dabb14610545578063372a26cb1461055a5780633ca5b2341461056f5761038c565b806322320c98146104f15780632d3b5207146105065780632e707bd21461051b5761038c565b80631388b4201161036a5780631388b4201461049d57806313989140146104b25780632113240d146104c757806321a360f5146104dc5761038c565b806305d8aa0a1461039b578063085e2c5b146103c257806312dea1601461046c575b3332141561039957600080fd5b005b3480156103a757600080fd5b506103b06109ea565b60408051918252519081900360200190f35b3480156103ce57600080fd5b50610411600480360360a08110156103e557600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608001356109f1565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b8381101561045757818101518382015260200161043f565b50505050905001935050505060405180910390f35b34801561047857600080fd5b50610481610b3d565b604080516001600160a01b039092168252519081900360200190f35b3480156104a957600080fd5b50610481610b55565b3480156104be57600080fd5b506103b0610b6d565b3480156104d357600080fd5b506103b0610b73565b3480156104e857600080fd5b506103b0610b79565b3480156104fd57600080fd5b50610481610b82565b34801561051257600080fd5b506103b0610b9a565b34801561052757600080fd5b506103b0610ba3565b34801561053c57600080fd5b50610481610ba8565b34801561055157600080fd5b506103b0610bc0565b34801561056657600080fd5b50610481610bc5565b34801561057b57600080fd5b50610481610bdd565b34801561059057600080fd5b50610481610bf5565b3480156105a557600080fd5b50610481610c07565b3480156105ba57600080fd5b506103b0610c1f565b3480156105cf57600080fd5b50610481610c27565b3480156105e457600080fd5b506103b0610c3f565b3480156105f957600080fd5b506103b0610c44565b34801561060e57600080fd5b50610481610c49565b34801561062357600080fd5b50610481610c61565b34801561063857600080fd5b506103b0610c79565b34801561064d57600080fd5b506103b0610c7f565b34801561066257600080fd5b506103b0610c85565b34801561067757600080fd5b506103b0610c8a565b34801561068c57600080fd5b506103b0610c91565b3480156106a157600080fd5b506103b0610c98565b3480156106b657600080fd5b506103b0610c9f565b3480156106cb57600080fd5b50610481610ca5565b3480156106e057600080fd5b506103b0610cb8565b3480156106f557600080fd5b506103b0610cbd565b34801561070a57600080fd5b50610481610cc4565b34801561071f57600080fd5b50610481610cdc565b34801561073457600080fd5b506103b0610cf4565b34801561074957600080fd5b50610481610cfc565b34801561075e57600080fd5b506103b0610d14565b34801561077357600080fd5b50610481610d1c565b34801561078857600080fd5b50610481610d34565b34801561079d57600080fd5b506103b0610d4c565b3480156107b257600080fd5b506103b0610d52565b3480156107c757600080fd5b50610481610d5b565b3480156107dc57600080fd5b506103b0610d73565b3480156107f157600080fd5b506103b0610d78565b34801561080657600080fd5b50610481610d7e565b34801561081b57600080fd5b506103b0610d96565b34801561083057600080fd5b506103b0610d9d565b34801561084557600080fd5b506103b0610da4565b34801561085a57600080fd5b50610481610da9565b34801561086f57600080fd5b506103b0610dc1565b34801561088457600080fd5b506103b0610dc9565b34801561089957600080fd5b50610481610dd0565b3480156108ae57600080fd5b50610481610de8565b3480156108c357600080fd5b506103b0610e00565b610399600480360360c08110156108e257600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a08101608082013564010000000081111561092257600080fd5b82018360208201111561093457600080fd5b8035906020019184602083028401116401000000008311171561095657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610e06915050565b3480156109a257600080fd5b50610481611028565b3480156109b757600080fd5b506103b061103a565b3480156109cc57600080fd5b5061048161103f565b3480156109e157600080fd5b50610481611057565b6220000081565b600080546040805163085e2c5b60e01b81526001600160a01b03898116600483015288811660248301526044820188905260648201879052608482018690529151606093929092169163085e2c5b9160a4808201928792909190829003018186803b158015610a5f57600080fd5b505afa158015610a73573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610a9c57600080fd5b815160208301805160405192949293830192919084640100000000821115610ac357600080fd5b908301906020820185811115610ad857600080fd5b8251866020820283011164010000000082111715610af557600080fd5b82525081516020918201928201910280838360005b83811015610b22578181015183820152602001610b0a565b50505050905001604052505050915091509550959350505050565b7352ae12abe5d8bd778bd5397f99ca900624cfadd481565b73794e6e91555438afc3ccf1c5076a74f42133d08d81565b61200081565b61800081565b64020000000081565b73a5407eae9ba41422680e2e00537571bcc53efbfd81565b64010000000081565b608081565b73dac17f958d2ee523a2206206994597c13d831ec781565b604081565b7379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2781565b734fabb145d64652a948d72533023f6e7a623c7c5381565b600080516020613ec183398151915281565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c81565b630100000081565b7345f783cce6b7ff23b2ab2d70e416cdb7d6055f5181565b601081565b602081565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b73a2b47e3d5c44877cca798226b7b8118f9bfb7a5681565b61400081565b61080081565b600881565b6202000081565b6210000081565b6208000081565b61040081565b6e085d4780b73119b644ae5ecd22b37681565b600281565b6240000081565b73398ec7346dcd622edc5ae82352f02be94c62d11981565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce31581565b630400000081565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9581565b630800000081565b734ddc2d193948926d02f9b1fe9e1daa0718270ed581565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b7306af07097c9eeb7fd685c692751d5c66db49c21581565b600181565b61020081565b7357ab1ec28d129707052df4df418d58a2d46d5f5181565b6280000081565b6204000081565b600c81565b7352ea46506b9cc5ef470c5bf89f17dc28bb35d85c81565b630200000081565b6201000081565b73f6e2d7f616b67e46d708e4410746e9aab3a4c51881565b73818e6fecd516ecc3849daf6845e3ec868087b75581565b61010081565b846001600160a01b0316866001600160a01b03161415610e2557611020565b610e2d613e25565b60405180610180016040528061106681526020016112e78152602001611472815260200161148f815260200161176881526020016118f38152602001611ac48152602001611ce98152602001611f18815260200161214781526020016122e581526020016124918152509050600c83511115610eda5760405162461bcd60e51b8152600401808060200182810382526042815260200180613f416042913960600191505060405180910390fd5b600080805b8551811015610f38576000868281518110610ef657fe5b60200260200101511115610f3057610f2a868281518110610f1357fe5b6020026020010151846125fd90919063ffffffff16565b92508091505b600101610edf565b5060008211610f785760405162461bcd60e51b815260040180806020018281038252602f815260200180613e71602f913960400191505060405180910390fd5b8660005b865181101561101a57868181518110610f9157fe5b602002602001015160001415610fa657611012565b6000610fde85610fd28a8581518110610fbb57fe5b60200260200101518d61266090919063ffffffff16565b9063ffffffff6126b916565b905083821415610feb5750815b808303925061100f8c8c838986600c811061100257fe5b602002015163ffffffff16565b50505b600101610f7c565b50505050505b505050505050565b600080516020613e5183398151915281565b600481565b733d9819210a31b4961b30ef54be2aed79b9c9cd3b81565b6000546001600160a01b031681565b60008161107b6001600160a01b0386166126fb565b6111ab57604080516303795fb160e11b81526001600160a01b0387166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156110dd57600080fd5b505afa1580156110f1573d6000803e3d6000fd5b505050506040513d602081101561110757600080fd5b505190506001600160a01b038116156111a9576111248682612737565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561117a57600080fd5b505af115801561118e573d6000803e3d6000fd5b505050506040513d60208110156111a457600080fd5b505191505b505b6111bd846001600160a01b03166126fb565b6112dd57604080516303795fb160e11b81526001600160a01b0386166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b15801561121f57600080fd5b505afa158015611233573d6000803e3d6000fd5b505050506040513d602081101561124957600080fd5b505190506001600160a01b038116156112db57806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b1580156112ab57600080fd5b505af11580156112bf573d6000803e3d6000fd5b50505050506040513d60208110156112d657600080fd5b505191505b505b90505b9392505050565b60006113078473818e6fecd516ecc3849daf6845e3ec868087b755612737565b73818e6fecd516ecc3849daf6845e3ec868087b7556329589f616113336001600160a01b0387166126fb565b61133e576000611340565b835b611352876001600160a01b03166126fb565b61135c5786611372565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b85611385886001600160a01b03166126fb565b61138f57876113a5565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b15801561143d57600080fd5b505af1158015611451573d6000803e3d6000fd5b50505050506040513d602081101561146857600080fd5b5051949350505050565b6000806114808585856127f0565b9050600081116112dd57600080fd5b60006114a3846001600160a01b03166126fb565b156115115773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156114f757600080fd5b505af115801561150b573d6000803e3d6000fd5b50505050505b611560611526856001600160a01b03166126fb565b6115305784611546565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b73794e6e91555438afc3ccf1c5076a74f42133d08d612737565b600073794e6e91555438afc3ccf1c5076a74f42133d08d630621b4f661158e6001600160a01b0388166126fb565b61159857866115ae565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b856115c1886001600160a01b03166126fb565b6115cb57876115e1565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b15801561163f57600080fd5b505af1158015611653573d6000803e3d6000fd5b505050506040513d602081101561166957600080fd5b5051905061167f6001600160a01b0385166126fb565b156112dd57604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b1580156116dd57600080fd5b505afa1580156116f1573d6000803e3d6000fd5b505050506040513d602081101561170757600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561174757600080fd5b505af115801561175b573d6000803e3d6000fd5b5050505090509392505050565b6000806001600160a01b038516600080516020613ec183398151915214611790576000611793565b60025b6001600160a01b038616600080516020613e51833981519152146117b85760006117bb565b60015b0160ff1690506000600080516020613ec18339815191526001600160a01b038616146117e85760006117eb565b60025b6001600160a01b038616600080516020613e5183398151915214611810576000611813565b60015b0160ff16905081600f0b6000148061182e575080600f0b6000145b1561183e576000925050506112e0565b61185c8673a2b47e3d5c44877cca798226b7b8118f9bfb7a56612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a2b47e3d5c44877cca798226b7b8118f9bfb7a569263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b505af11580156118e6573d6000803e3d6000fd5b5050505050509392505050565b6000806001600160a01b03851673dac17f958d2ee523a2206206994597c13d831ec714611921576000611924565b60035b6001600160a01b038616600080516020613ec18339815191521461194957600061194c565b60025b6001600160a01b038716600080516020613e5183398151915214611971576000611974565b60015b010160ff169050600073dac17f958d2ee523a2206206994597c13d831ec76001600160a01b0316856001600160a01b0316146119b15760006119b4565b60035b6001600160a01b038616600080516020613ec1833981519152146119d95760006119dc565b60025b6001600160a01b038716600080516020613e5183398151915214611a01576000611a04565b60015b010160ff16905081600f0b60001480611a20575080600f0b6000145b15611a30576000925050506112e0565b611a4e867352ea46506b9cc5ef470c5bf89f17dc28bb35d85c612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517352ea46506b9cc5ef470c5bf89f17dc28bb35d85c9263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b6000806001600160a01b0385166e085d4780b73119b644ae5ecd22b37614611aed576000611af0565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611b1b576000611b1e565b60035b6001600160a01b038716600080516020613ec183398151915214611b43576000611b46565b60025b6001600160a01b038816600080516020613e5183398151915214611b6b576000611b6e565b60015b01010160ff16905060006e085d4780b73119b644ae5ecd22b3766001600160a01b0316856001600160a01b031614611ba7576000611baa565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611bd5576000611bd8565b60035b6001600160a01b038716600080516020613ec183398151915214611bfd576000611c00565b60025b6001600160a01b038816600080516020613e5183398151915214611c25576000611c28565b60015b01010160ff16905081600f0b60001480611c45575080600f0b6000145b15611c55576000925050506112e0565b611c73867345f783cce6b7ff23b2ab2d70e416cdb7d6055f51612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517345f783cce6b7ff23b2ab2d70e416cdb7d6055f519263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b6000806001600160a01b038516734fabb145d64652a948d72533023f6e7a623c7c5314611d17576000611d1a565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611d45576000611d48565b60035b6001600160a01b038716600080516020613ec183398151915214611d6d576000611d70565b60025b6001600160a01b038816600080516020613e5183398151915214611d95576000611d98565b60015b01010160ff1690506000734fabb145d64652a948d72533023f6e7a623c7c536001600160a01b0316856001600160a01b031614611dd6576000611dd9565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611e04576000611e07565b60035b6001600160a01b038716600080516020613ec183398151915214611e2c576000611e2f565b60025b6001600160a01b038816600080516020613e5183398151915214611e54576000611e57565b60015b01010160ff16905081600f0b60001480611e74575080600f0b6000145b15611e84576000925050506112e0565b611ea2867379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517379a8c46dea5ada233abaffd40f3a0a2b1e5a4f279263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b6000806001600160a01b0385167357ab1ec28d129707052df4df418d58a2d46d5f5114611f46576000611f49565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611f74576000611f77565b60035b6001600160a01b038716600080516020613ec183398151915214611f9c576000611f9f565b60025b6001600160a01b038816600080516020613e5183398151915214611fc4576000611fc7565b60015b01010160ff16905060007357ab1ec28d129707052df4df418d58a2d46d5f516001600160a01b0316856001600160a01b031614612005576000612008565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714612033576000612036565b60035b6001600160a01b038716600080516020613ec18339815191521461205b57600061205e565b60025b6001600160a01b038816600080516020613e5183398151915214612083576000612086565b60015b01010160ff16905081600f0b600014806120a3575080600f0b6000145b156120b3576000925050506112e0565b6120d18673a5407eae9ba41422680e2e00537571bcc53efbfd612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a5407eae9ba41422680e2e00537571bcc53efbfd9263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b600061215b846001600160a01b03166126fb565b61221457600061216a85612bde565b90506121768582612737565b806001600160a01b031663a0712d68846040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156121bc57600080fd5b505af11580156121d0573d6000803e3d6000fd5b505050506040513d60208110156121e657600080fd5b5061220c905081856122076001600160a01b0383163063ffffffff612dce16565b611066565b9150506112e0565b612226836001600160a01b03166126fb565b6122db57600061223584612bde565b90506000612244868386611066565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561228c57600080fd5b505af11580156122a0573d6000803e3d6000fd5b505050506040513d60208110156122b657600080fd5b506122d290506001600160a01b0386163063ffffffff612dce16565b925050506112e0565b5060009392505050565b60006001600160a01b038416600080516020613e5183398151915214156123c857612324847306af07097c9eeb7fd685c692751d5c66db49c215612737565b60408051633b4da69f60e01b81523060048201526024810184905290517306af07097c9eeb7fd685c692751d5c66db49c21591633b4da69f91604480830192600092919082900301818387803b15801561237d57600080fd5b505af1158015612391573d6000803e3d6000fd5b506123c192507306af07097c9eeb7fd685c692751d5c66db49c2159150859050612207823063ffffffff612dce16565b90506112e0565b6001600160a01b038316600080516020613e5183398151915214156122db576000612408857306af07097c9eeb7fd685c692751d5c66db49c21585611066565b6040805163ef693bed60e01b81523060048201526024810183905290519192507306af07097c9eeb7fd685c692751d5c66db49c2159163ef693bed9160448082019260009290919082900301818387803b15801561246557600080fd5b505af1158015612479573d6000803e3d6000fd5b5061220c925050506001600160a01b03851630612dce565b60006124a5846001600160a01b03166126fb565b6125635760006124b485612e78565b90506124c08582612737565b60408051636968703360e11b81526001600160a01b03871660048201526024810185905261044d6044820152905173398ec7346dcd622edc5ae82352f02be94c62d1199163d2d0e06691606480830192600092919082900301818387803b15801561252a57600080fd5b505af115801561253e573d6000803e3d6000fd5b5050505061220c818561220730856001600160a01b0316612dce90919063ffffffff16565b612575836001600160a01b03166126fb565b6122db57600061258484612e78565b90506000612593868386611066565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156125db57600080fd5b505af11580156125ef573d6000803e3d6000fd5b5050505080925050506112e0565b600082820183811015612657576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60008261266f5750600061265a565b8282028284828161267c57fe5b04146126575760405162461bcd60e51b8152600401808060200182810382526021815260200180613ea06021913960400191505060405180910390fd5b600061265783836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061329b565b60006001600160a01b038216158061272f57506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b90505b919050565b612749826001600160a01b03166126fb565b6127ec5760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b15801561279e57600080fd5b505afa1580156127b2573d6000803e3d6000fd5b505050506040513d60208110156127c857600080fd5b5051901c6127ec576127ec6001600160a01b0383168260001963ffffffff61333d16565b5050565b6000612804846001600160a01b03166126fb565b156128725773c0829421c1d260bd3cb3e0f06cfe2d52db2ce3156001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561285857600080fd5b505af115801561286c573d6000803e3d6000fd5b50505050505b60007352ae12abe5d8bd778bd5397f99ca900624cfadd46001600160a01b031663bb34534c6040518163ffffffff1660e01b815260040180806c42616e636f724e6574776f726b60981b815250602001905060206040518083038186803b1580156128dc57600080fd5b505afa1580156128f0573d6000803e3d6000fd5b505050506040513d602081101561290657600080fd5b5051905060606129168686613413565b905061295361292d876001600160a01b03166126fb565b612937578661294d565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b83612737565b60006060836001600160a01b03166216e360856001600160a01b031663c7ba24bc905060e01b8589600160405160240180806020018481526020018360ff168152602001828103825285818151815260200191508051906020019060200280838360005b838110156129cf5781810151838201526020016129b7565b50505050905001945050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0383818316178352505050506040518082805190602001908083835b60208310612a3e5780518252601f199092019160209182019101612a1f565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038160008787f1925050503d8060008114612aa1576040519150601f19603f3d011682016040523d82523d6000602084013e612aa6565b606091505b5091509150600082612ab9576000612ad1565b818060200190516020811015612ace57600080fd5b50515b9050612ae5886001600160a01b03166126fb565b8015612af15750600081115b15612bd257604080516370a0823160e01b8152306004820152905173c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591632e1a7d4d9183916370a08231916024808301926020929190829003018186803b158015612b4f57600080fd5b505afa158015612b63573d6000803e3d6000fd5b505050506040513d6020811015612b7957600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015612bb957600080fd5b505af1158015612bcd573d6000803e3d6000fd5b505050505b98975050505050505050565b6000612bf2826001600160a01b03166126fb565b15612c125750734ddc2d193948926d02f9b1fe9e1daa0718270ed5612732565b6001600160a01b038216600080516020613e518339815191521415612c4c5750735d3a536e4d6dbd6114cc1ead35777bab948e3643612732565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415612c8c5750736c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e612732565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e8621415612ccc575073158079ee67fce2f58472a96584a73c7ab9ac95c1612732565b6001600160a01b038216600080516020613ec18339815191521415612d0657507339aa39c021dfbae8fac545936693ac917d5e7563612732565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415612d46575073c11b1268c1a384e55c48c2391d8d480264a3a7f4612732565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415612d86575073b3319f5d18bc0d84dd1b4825dcde5d5f7266d407612732565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec71415612dc6575073f650c3d88d12db855b8bf7d11be6c55a4e07dcc9612732565b506000919050565b6000612dd9836126fb565b15612def57506001600160a01b0381163161265a565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015612e4557600080fd5b505afa158015612e59573d6000803e3d6000fd5b505050506040513d6020811015612e6f57600080fd5b5051905061265a565b6000612e8c826001600160a01b03166126fb565b15612eac5750733a3a65aab0dd2a17e3f1947ba16138cd37d08c04612732565b6001600160a01b038216600080516020613e518339815191521415612ee6575073fc1e690f61efd961294b3e1ce3313fbd8aa4f85d612732565b6001600160a01b038216600080516020613ec18339815191521415612f205750739ba00d6856a4edf4665bca2c2309936572473b7e612732565b6001600160a01b0382167357ab1ec28d129707052df4df418d58a2d46d5f511415612f60575073625ae63000f46200499120b906716420bd059240612732565b6001600160a01b038216734fabb145d64652a948d72533023f6e7a623c7c531415612fa05750736ee0f7bb50a54ab5253da0667b0dc2ee526c30a8612732565b6001600160a01b0382166e085d4780b73119b644ae5ecd22b3761415612fdb5750734da9b813057d04baef4e5800e36083717b4a0341612732565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec7141561301b57507371fc860f7d3a592a4a98740e39db31d25db65ae8612732565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef141561305b575073e1ba0fb44ccb0d11b80f92f4f8ed94ca3ff51d00612732565b6001600160a01b03821673dd974d5c2e2928dea5f71b9825b8b646686bd200141561309b5750739d91be44c06d373a8a226e1f3b146956083803eb612732565b6001600160a01b0382167380fb784b7ed66730e8b1dbd9820afd29931aab0314156130db5750737d2d3688df45ce7c552e19c27e007673da9204b8612732565b6001600160a01b03821673514910771af9ca656af840dff83e8264ecf986ca141561311b575073a64bd6c70cb9051f6a9ba1f163fdc07e0dfb5f84612732565b6001600160a01b038216730f5d2fb29fb7d3cfee444a200298f468908cc942141561315b5750736fce4a401b6b80ace52baaefe4421bd188e76f6f612732565b6001600160a01b038216739f8f72aa9304c8b593d555f12ef6589cc3a579a2141561319b5750737deb5e830be29f91e298ba5ff1356bb7f8146998612732565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e86214156131db57507371010a9d003445ac60c4e6a7017c1e89a477b438612732565b6001600160a01b03821673c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f141561321b575073328c4c80bc7aca0834db37e6600a6c49e12da4de612732565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c599141561325b575073fc4b8ed459e00e5400be803a9bb3954234fd50e3612732565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415612dc65750736fb0855c404e09c47c3fbca25f08d4e41f9f062f612732565b600081836133275760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156132ec5781810151838201526020016132d4565b50505050905090810190601f1680156133195780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161333357fe5b0495945050505050565b613346836126fb565b61340e576000811180156133d4575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b1580156133a657600080fd5b505afa1580156133ba573d6000803e3d6000fd5b505050506040513d60208110156133d057600080fd5b5051115b156133f4576133f46001600160a01b03841683600063ffffffff613b1d16565b61340e6001600160a01b038416838363ffffffff613b1d16565b505050565b6060816001600160a01b0316836001600160a01b03161415613444575060408051600081526020810190915261265a565b613456836001600160a01b03166126fb565b156134735773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31592505b613485826001600160a01b03166126fb565b156134a25773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591505b6001600160a01b038316731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14806134e957506001600160a01b038216731f573d6fb3f13d689ff844b4ce37794d79a7ff1c145b1561351457604080516003808252608082019092529060208201606080388339019050509050613536565b60408051600580825260c08201909252906020820160a0803883390190505090505b6000806001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c146136ff576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6135936001600160a01b038b166126fb565b61359d57896135b3565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106136315780518252601f199092019160209182019101613612565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613692576040519150601f19603f3d011682016040523d82523d6000602084013e613697565b606091505b5091509150816136bf5760408051600080825260208201909252905b5094505050505061265a565b8080602001905160208110156136d457600080fd5b505193506001600160a01b0384166136fc5760408051600080825260208201909252906136b3565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c146138bd576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6137596001600160a01b038a166126fb565b6137635788613779565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106137f75780518252601f1990920191602091820191016137d8565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613858576040519150601f19603f3d011682016040523d82523d6000602084013e61385d565b606091505b50915091508161387d5760408051600080825260208201909252906136b3565b80806020019051602081101561389257600080fd5b505192506001600160a01b0383166138ba5760408051600080825260208201909252906136b3565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156139805784836000815181106138f057fe5b60200260200101906001600160a01b031690816001600160a01b031681525050818360018151811061391e57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360028151811061396057fe5b6001600160a01b03909216602092830291909101909101525061265a9050565b6001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c1415613a2357731f573d6fb3f13d689ff844b4ce37794d79a7ff1c836000815181106139c757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505080836001815181106139f557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050838360028151811061396057fe5b8483600081518110613a3157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508183600181518110613a5f57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c83600281518110613aa157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508083600381518110613acf57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508383600481518110613afd57fe5b6001600160a01b0390921660209283029190910190910152505092915050565b801580613ba3575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b158015613b7557600080fd5b505afa158015613b89573d6000803e3d6000fd5b505050506040513d6020811015613b9f57600080fd5b5051155b613bde5760405162461bcd60e51b8152600401808060200182810382526036815260200180613f0b6036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261340e908490613c3d826001600160a01b0316613de9565b613c8e576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310613ccc5780518252601f199092019160209182019101613cad565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613d2e576040519150601f19603f3d011682016040523d82523d6000602084013e613d33565b606091505b509150915081613d8a576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115613de357808060200190516020811015613da657600080fd5b5051613de35760405162461bcd60e51b815260040180806020018281038252602a815260200180613ee1602a913960400191505060405180910390fd5b50505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590613e1d57508115155b949350505050565b604051806101800160405280600c905b613e4e815260200190600190039081613e355790505090565bfefe0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f73536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e63654f6e6553706c69743a20446973747269627574696f6e2061727261792073686f756c64206e6f74206578636565642072657365727665732061727261792073697a65a265627a7a72315820ca5088fe2af0082cd092e41fef403a00ef3cb50b20866f719ac8ea952578659264736f6c63430005110032 \ No newline at end of file diff --git a/OneSplit.full.sol b/OneSplit.full.sol index 2e4228a..57c123a 100644 --- a/OneSplit.full.sol +++ b/OneSplit.full.sol @@ -133,6 +133,7 @@ contract IOneSplitConsts { uint256 public constant FLAG_DISABLE_UNISWAP_POOL_TOKEN = 0x1000000; uint256 public constant FLAG_DISABLE_BALANCER_POOL_TOKEN = 0x2000000; uint256 public constant FLAG_DISABLE_CURVE_ZAP = 0x4000000; + uint256 public constant FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN = 0x8000000; } contract IOneSplit is IOneSplitConsts { @@ -4349,7 +4350,7 @@ pragma solidity ^0.5.0; contract OneSplitUniswapPoolTokenBase { using SafeMath for uint256; - IUniswapFactory uniswapFactory = IUniswapFactory(0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95); + IUniswapFactory constant uniswapFactory = IUniswapFactory(0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95); function isLiquidityPool(IERC20 token) internal view returns (bool) { return address(uniswapFactory.getToken(address(token))) != address(0); @@ -5820,6 +5821,443 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { } } +// File: contracts/interface/IUniswapV2Pair.sol + +pragma solidity ^0.5.0; + + +interface IUniswapV2Pair { + function factory() external view returns (address); + + function token0() external view returns (IERC20); + function token1() external view returns (IERC20); + + function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); + + function mint(address to) external returns (uint liquidity); + function burn(address to) external returns (uint amount0, uint amount1); +} + +// File: contracts/interface/IUniswapV2Pool.sol + +pragma solidity ^0.5.0; + + +interface IUniswapV2Pool { + function addLiquidity( + IUniswapV2Pair pool, + uint256[2] calldata amounts, + uint256 minMintAmount + ) + external + returns (uint256); + + function removeLiquidity( + IUniswapV2Pair pool, + uint256 burnAmount, + uint256[2] calldata minReturnAmount + ) + external + returns (uint256[2] memory); +} + +// File: contracts/OneSplitUniswapV2PoolToken.sol + +pragma solidity ^0.5.0; + + + + + +contract OneSplitUniswapV2PoolTokenBase { + using SafeMath for uint256; + + IUniswapV2Pool constant uniswapPool = IUniswapV2Pool(0x3f6CDd93e4A1c2Df9934Cb90D09040CcFc155F93); + + function isLiquidityPool(IERC20 token) internal view returns (bool) { + return IUniswapV2Pair(address(token)).factory() == 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; + } + + struct TokenInfo { + IERC20 token; + uint256 reserve; + } + + struct PoolDetails { + TokenInfo[2] tokens; + uint256 totalSupply; + } + + function _getPoolDetails(IUniswapV2Pair pair) internal view returns (PoolDetails memory details) { + (uint112 reserve0, uint112 reserve1, ) = pair.getReserves(); + + details.tokens[0] = TokenInfo({ + token: pair.token0(), + reserve: reserve0 + }); + details.tokens[1] = TokenInfo({ + token: pair.token1(), + reserve: reserve1 + }); + + details.totalSupply = IERC20(address(pair)).totalSupply(); + } + +} + +contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswapV2PoolTokenBase { + + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + public + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + if (fromToken == toToken) { + return (amount, new uint256[](DEXES_COUNT)); + } + + + if (!flags.check(FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN)) { + bool isPoolTokenFrom = isLiquidityPool(fromToken); + bool isPoolTokenTo = isLiquidityPool(toToken); + + if (isPoolTokenFrom && isPoolTokenTo) { + ( + uint256 returnWETHAmount, + uint256[] memory poolTokenFromDistribution + ) = _getExpectedReturnFromUniswapV2PoolToken( + fromToken, + wethToken, + amount, + parts, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + + ( + uint256 returnPoolTokenToAmount, + uint256[] memory poolTokenToDistribution + ) = _getExpectedReturnToUniswapV2PoolToken( + wethToken, + toToken, + returnWETHAmount, + parts, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + + for (uint i = 0; i < poolTokenToDistribution.length; i++) { + poolTokenFromDistribution[i] |= poolTokenToDistribution[i] << 128; + } + + return (returnPoolTokenToAmount, poolTokenFromDistribution); + } + + if (isPoolTokenFrom) { + return _getExpectedReturnFromUniswapV2PoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _getExpectedReturnToUniswapV2PoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + } + + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + flags + ); + } + + function _getExpectedReturnFromUniswapV2PoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); + for (uint i = 0; i < 2; i++) { + + uint256 exchangeAmount = amount + .mul(details.tokens[i].reserve) + .div(details.totalSupply); + + if (toToken == details.tokens[i].token) { + returnAmount = returnAmount.add(exchangeAmount); + continue; + } + + (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( + details.tokens[i].token, + toToken, + exchangeAmount, + parts, + flags + ); + + returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + return (returnAmount, distribution); + } + + function _getExpectedReturnToUniswapV2PoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); + + uint256[2] memory amounts; + amounts[0] = amount.div(2); + amounts[1] = amount.sub(amounts[0]); + for (uint i = 0; i < 2; i++) { + + if (fromToken == details.tokens[i].token) { + uint256 liquidity = amounts[i].mul(details.totalSupply).div(details.tokens[i].reserve); + returnAmount = liquidity > returnAmount ? liquidity : returnAmount; + continue; + } + + (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( + fromToken, + details.tokens[i].token, + amounts[i], + parts, + flags + ); + + uint256 liquidity = ret.mul(details.totalSupply).div(details.tokens[i].reserve); + returnAmount = liquidity > returnAmount ? liquidity : returnAmount; + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + return ( + returnAmount, + distribution + ); + } + +} + + +contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) internal { + if (fromToken == toToken) { + return; + } + + if (!flags.check(FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN)) { + bool isPoolTokenFrom = isLiquidityPool(fromToken); + bool isPoolTokenTo = isLiquidityPool(toToken); + + if (isPoolTokenFrom && isPoolTokenTo) { + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] & ((1 << 128) - 1); + } + + uint256 wEthBalanceBefore = wethToken.balanceOf(address(this)); + + _swapFromUniswapV2PoolToken( + fromToken, + wethToken, + amount, + dist, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] >> 128; + } + + uint256 wEthBalanceAfter = wethToken.balanceOf(address(this)); + + return _swapToUniswapV2PoolToken( + wethToken, + toToken, + wEthBalanceAfter.sub(wEthBalanceBefore), + dist, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + + if (isPoolTokenFrom) { + return _swapFromUniswapV2PoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _swapToUniswapV2PoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + } + + return super._swap( + fromToken, + toToken, + amount, + distribution, + flags + ); + } + + function _swapFromUniswapV2PoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + _infiniteApproveIfNeeded(poolToken, address(uniswapPool)); + + uint256[2] memory amounts = uniswapPool.removeLiquidity( + IUniswapV2Pair(address(poolToken)), + amount, + [ + uint256(0), + uint256(0) + ] + ); + + uint256[] memory dist = new uint256[](distribution.length); + + PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); + for (uint i = 0; i < 2; i++) { + + if (toToken == details.tokens[i].token) { + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + super._swap( + details.tokens[i].token, + toToken, + amounts[i], + dist, + flags + ); + } + } + + function _swapToUniswapV2PoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + uint256[] memory dist = new uint256[](distribution.length); + + distribution = new uint256[](DEXES_COUNT); + + PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); + + // will overwritten to liquidity amounts + uint256[2] memory amounts; + amounts[0] = amount.div(2); + amounts[1] = amount.sub(amounts[0]); + for (uint i = 0; i < 2; i++) { + + _infiniteApproveIfNeeded(details.tokens[i].token, address(uniswapPool)); + + if (fromToken == details.tokens[i].token) { + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + super._swap( + fromToken, + details.tokens[i].token, + amounts[i], + dist, + flags + ); + + amounts[i] = details.tokens[i].token.universalBalanceOf(address(this)); + } + + uniswapPool.addLiquidity(IUniswapV2Pair(address(poolToken)), amounts, 0); + + for (uint i = 0; i < 2; i++) { + details.tokens[i].token.universalTransfer( + msg.sender, + details.tokens[i].token.universalBalanceOf(address(this)) + ); + } + } +} + // File: contracts/OneSplit.sol pragma solidity ^0.5.0; @@ -5840,21 +6278,23 @@ pragma solidity ^0.5.0; + contract OneSplitViewWrap is OneSplitViewWrapBase, - //OneSplitMultiPathView, - //OneSplitChaiView, - //OneSplitBdaiView, - //OneSplitAaveView, - //OneSplitFulcrumView, + OneSplitMultiPathView, + OneSplitChaiView, + OneSplitBdaiView, + OneSplitAaveView, + OneSplitFulcrumView, OneSplitCompoundView, OneSplitIearnView, - //OneSplitIdleView, + OneSplitIdleView, OneSplitWethView, - //OneSplitBalancerPoolTokenView, + OneSplitBalancerPoolTokenView, OneSplitUniswapPoolTokenView, OneSplitCurvePoolTokenView, - OneSplitSmartTokenView + OneSplitSmartTokenView, + OneSplitUniswapV2PoolTokenView { IOneSplitView public oneSplitView; @@ -5930,19 +6370,20 @@ contract OneSplitViewWrap is contract OneSplitWrap is OneSplitBaseWrap, - //OneSplitMultiPath, - //OneSplitChai, - //OneSplitBdai, - //OneSplitAave, - //OneSplitFulcrum, + OneSplitMultiPath, + OneSplitChai, + OneSplitBdai, + OneSplitAave, + OneSplitFulcrum, OneSplitCompound, OneSplitIearn, - //OneSplitIdle, + OneSplitIdle, OneSplitWeth, - //OneSplitBalancerPoolToken, + OneSplitBalancerPoolToken, OneSplitUniswapPoolToken, OneSplitCurvePoolToken, - OneSplitSmartToken + OneSplitSmartToken, + OneSplitUniswapV2PoolToken { IOneSplitView public oneSplitView; IOneSplit public oneSplit; diff --git a/contracts/IOneSplit.sol b/contracts/IOneSplit.sol index a45c22f..a66de38 100644 --- a/contracts/IOneSplit.sol +++ b/contracts/IOneSplit.sol @@ -52,6 +52,7 @@ contract IOneSplitConsts { uint256 public constant FLAG_DISABLE_UNISWAP_POOL_TOKEN = 0x1000000; uint256 public constant FLAG_DISABLE_BALANCER_POOL_TOKEN = 0x2000000; uint256 public constant FLAG_DISABLE_CURVE_ZAP = 0x4000000; + uint256 public constant FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN = 0x8000000; } contract IOneSplit is IOneSplitConsts { diff --git a/contracts/OneSplit.sol b/contracts/OneSplit.sol index 1bc1c39..bdeb51e 100644 --- a/contracts/OneSplit.sol +++ b/contracts/OneSplit.sol @@ -15,6 +15,7 @@ import "./OneSplitBalancerPoolToken.sol"; import "./OneSplitUniswapPoolToken.sol"; import "./OneSplitCurvePoolToken.sol"; import "./OneSplitSmartToken.sol"; +import "./OneSplitUniswapV2PoolToken.sol"; contract OneSplitViewWrap is @@ -31,7 +32,8 @@ contract OneSplitViewWrap is OneSplitBalancerPoolTokenView, OneSplitUniswapPoolTokenView, OneSplitCurvePoolTokenView, - OneSplitSmartTokenView + OneSplitSmartTokenView, + OneSplitUniswapV2PoolTokenView { IOneSplitView public oneSplitView; @@ -119,7 +121,8 @@ contract OneSplitWrap is OneSplitBalancerPoolToken, OneSplitUniswapPoolToken, OneSplitCurvePoolToken, - OneSplitSmartToken + OneSplitSmartToken, + OneSplitUniswapV2PoolToken { IOneSplitView public oneSplitView; IOneSplit public oneSplit; diff --git a/contracts/OneSplitUniswapPoolToken.sol b/contracts/OneSplitUniswapPoolToken.sol index 56b6e3c..48a99eb 100644 --- a/contracts/OneSplitUniswapPoolToken.sol +++ b/contracts/OneSplitUniswapPoolToken.sol @@ -8,7 +8,7 @@ import "./OneSplitBase.sol"; contract OneSplitUniswapPoolTokenBase { using SafeMath for uint256; - IUniswapFactory uniswapFactory = IUniswapFactory(0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95); + IUniswapFactory constant uniswapFactory = IUniswapFactory(0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95); function isLiquidityPool(IERC20 token) internal view returns (bool) { return address(uniswapFactory.getToken(address(token))) != address(0); diff --git a/contracts/OneSplitUniswapV2PoolToken.sol b/contracts/OneSplitUniswapV2PoolToken.sol new file mode 100644 index 0000000..f7dc2c9 --- /dev/null +++ b/contracts/OneSplitUniswapV2PoolToken.sol @@ -0,0 +1,395 @@ +pragma solidity ^0.5.0; + +import "./OneSplitBase.sol"; +import "./interface/IUniswapV2Pool.sol"; +import "./interface/IUniswapV2Pair.sol"; + + +contract OneSplitUniswapV2PoolTokenBase { + using SafeMath for uint256; + + IUniswapV2Pool constant uniswapPool = IUniswapV2Pool(0x3f6CDd93e4A1c2Df9934Cb90D09040CcFc155F93); + + function isLiquidityPool(IERC20 token) internal view returns (bool) { + return IUniswapV2Pair(address(token)).factory() == 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; + } + + struct TokenInfo { + IERC20 token; + uint256 reserve; + } + + struct PoolDetails { + TokenInfo[2] tokens; + uint256 totalSupply; + } + + function _getPoolDetails(IUniswapV2Pair pair) internal view returns (PoolDetails memory details) { + (uint112 reserve0, uint112 reserve1, ) = pair.getReserves(); + + details.tokens[0] = TokenInfo({ + token: pair.token0(), + reserve: reserve0 + }); + details.tokens[1] = TokenInfo({ + token: pair.token1(), + reserve: reserve1 + }); + + details.totalSupply = IERC20(address(pair)).totalSupply(); + } + +} + +contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswapV2PoolTokenBase { + + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + public + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + if (fromToken == toToken) { + return (amount, new uint256[](DEXES_COUNT)); + } + + + if (!flags.check(FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN)) { + bool isPoolTokenFrom = isLiquidityPool(fromToken); + bool isPoolTokenTo = isLiquidityPool(toToken); + + if (isPoolTokenFrom && isPoolTokenTo) { + ( + uint256 returnWETHAmount, + uint256[] memory poolTokenFromDistribution + ) = _getExpectedReturnFromUniswapV2PoolToken( + fromToken, + wethToken, + amount, + parts, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + + ( + uint256 returnPoolTokenToAmount, + uint256[] memory poolTokenToDistribution + ) = _getExpectedReturnToUniswapV2PoolToken( + wethToken, + toToken, + returnWETHAmount, + parts, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + + for (uint i = 0; i < poolTokenToDistribution.length; i++) { + poolTokenFromDistribution[i] |= poolTokenToDistribution[i] << 128; + } + + return (returnPoolTokenToAmount, poolTokenFromDistribution); + } + + if (isPoolTokenFrom) { + return _getExpectedReturnFromUniswapV2PoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _getExpectedReturnToUniswapV2PoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + } + + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + flags + ); + } + + function _getExpectedReturnFromUniswapV2PoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); + for (uint i = 0; i < 2; i++) { + + uint256 exchangeAmount = amount + .mul(details.tokens[i].reserve) + .div(details.totalSupply); + + if (toToken == details.tokens[i].token) { + returnAmount = returnAmount.add(exchangeAmount); + continue; + } + + (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( + details.tokens[i].token, + toToken, + exchangeAmount, + parts, + flags + ); + + returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + return (returnAmount, distribution); + } + + function _getExpectedReturnToUniswapV2PoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); + + uint256[2] memory amounts; + amounts[0] = amount.div(2); + amounts[1] = amount.sub(amounts[0]); + for (uint i = 0; i < 2; i++) { + + if (fromToken == details.tokens[i].token) { + uint256 liquidity = amounts[i].mul(details.totalSupply).div(details.tokens[i].reserve); + returnAmount = liquidity > returnAmount ? liquidity : returnAmount; + continue; + } + + (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( + fromToken, + details.tokens[i].token, + amounts[i], + parts, + flags + ); + + uint256 liquidity = ret.mul(details.totalSupply).div(details.tokens[i].reserve); + returnAmount = liquidity > returnAmount ? liquidity : returnAmount; + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + return ( + returnAmount, + distribution + ); + } + +} + + +contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) internal { + if (fromToken == toToken) { + return; + } + + if (!flags.check(FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN)) { + bool isPoolTokenFrom = isLiquidityPool(fromToken); + bool isPoolTokenTo = isLiquidityPool(toToken); + + if (isPoolTokenFrom && isPoolTokenTo) { + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] & ((1 << 128) - 1); + } + + uint256 wEthBalanceBefore = wethToken.balanceOf(address(this)); + + _swapFromUniswapV2PoolToken( + fromToken, + wethToken, + amount, + dist, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] >> 128; + } + + uint256 wEthBalanceAfter = wethToken.balanceOf(address(this)); + + return _swapToUniswapV2PoolToken( + wethToken, + toToken, + wEthBalanceAfter.sub(wEthBalanceBefore), + dist, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + + if (isPoolTokenFrom) { + return _swapFromUniswapV2PoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _swapToUniswapV2PoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + } + + return super._swap( + fromToken, + toToken, + amount, + distribution, + flags + ); + } + + function _swapFromUniswapV2PoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + _infiniteApproveIfNeeded(poolToken, address(uniswapPool)); + + uint256[2] memory amounts = uniswapPool.removeLiquidity( + IUniswapV2Pair(address(poolToken)), + amount, + [ + uint256(0), + uint256(0) + ] + ); + + uint256[] memory dist = new uint256[](distribution.length); + + PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); + for (uint i = 0; i < 2; i++) { + + if (toToken == details.tokens[i].token) { + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + super._swap( + details.tokens[i].token, + toToken, + amounts[i], + dist, + flags + ); + } + } + + function _swapToUniswapV2PoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + uint256[] memory dist = new uint256[](distribution.length); + + distribution = new uint256[](DEXES_COUNT); + + PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); + + // will overwritten to liquidity amounts + uint256[2] memory amounts; + amounts[0] = amount.div(2); + amounts[1] = amount.sub(amounts[0]); + for (uint i = 0; i < 2; i++) { + + _infiniteApproveIfNeeded(details.tokens[i].token, address(uniswapPool)); + + if (fromToken == details.tokens[i].token) { + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + super._swap( + fromToken, + details.tokens[i].token, + amounts[i], + dist, + flags + ); + + amounts[i] = details.tokens[i].token.universalBalanceOf(address(this)); + } + + uniswapPool.addLiquidity(IUniswapV2Pair(address(poolToken)), amounts, 0); + + for (uint i = 0; i < 2; i++) { + details.tokens[i].token.universalTransfer( + msg.sender, + details.tokens[i].token.universalBalanceOf(address(this)) + ); + } + } +} diff --git a/contracts/interface/IUniswapV2Pair.sol b/contracts/interface/IUniswapV2Pair.sol new file mode 100644 index 0000000..3a46df6 --- /dev/null +++ b/contracts/interface/IUniswapV2Pair.sol @@ -0,0 +1,15 @@ +pragma solidity ^0.5.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface IUniswapV2Pair { + function factory() external view returns (address); + + function token0() external view returns (IERC20); + function token1() external view returns (IERC20); + + function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); + + function mint(address to) external returns (uint liquidity); + function burn(address to) external returns (uint amount0, uint amount1); +} diff --git a/contracts/interface/IUniswapV2Pool.sol b/contracts/interface/IUniswapV2Pool.sol new file mode 100644 index 0000000..2767ee0 --- /dev/null +++ b/contracts/interface/IUniswapV2Pool.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.5.0; + +import "./IUniswapV2Pair.sol"; + +interface IUniswapV2Pool { + function addLiquidity( + IUniswapV2Pair pool, + uint256[2] calldata amounts, + uint256 minMintAmount + ) + external + returns (uint256); + + function removeLiquidity( + IUniswapV2Pair pool, + uint256 burnAmount, + uint256[2] calldata minReturnAmount + ) + external + returns (uint256[2] memory); +} From af1d4da15bb4eff342f9d8207add361612ac89fd Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 8 May 2020 06:31:58 +0300 Subject: [PATCH 54/60] some fixes --- OneSplit.full.bin | 2 +- OneSplit.full.sol | 124 +++++++++-------------- contracts/OneSplitSmartToken.sol | 68 +++---------- contracts/OneSplitUniswapV2PoolToken.sol | 16 ++- 4 files changed, 77 insertions(+), 133 deletions(-) diff --git a/OneSplit.full.bin b/OneSplit.full.bin index b570d64..308a34f 100644 --- a/OneSplit.full.bin +++ b/OneSplit.full.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b5060405161401c38038061401c8339818101604052602081101561003357600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055613fb7806100656000396000f3fe60806040526004361061038c5760003560e01c806375b5be2d116101dc578063c925777511610102578063d70a2d1f116100a0578063f4b9fa751161006f578063f4b9fa7514610996578063f56e281f146109ab578063f69e2046146109c0578063fbe4ed95146109d55761038c565b8063d70a2d1f1461088d578063d77366a4146108a2578063dc1536b2146108b7578063e2a7515e146108cc5761038c565b8063cc26e9fc116100dc578063cc26e9fc14610839578063cede5f6a1461084e578063d1aee5e314610863578063d393c3e9146108785761038c565b8063c9257775146107fa578063c989b6671461080f578063c9b42c67146108245761038c565b80638ea812c01161017a578063b3bc784411610149578063b3bc7844146107a6578063b69d0456146107bb578063c762a46c146107d0578063c77b9de6146107e55761038c565b80638ea812c014610752578063a1b4d01114610767578063a734f06e1461077c578063b0a7ef29146107915761038c565b8063819faf7b116101b6578063819faf7b146106fe578063851954fa146107135780638aea49d2146107285780638bdb2afa1461073d5761038c565b806375b5be2d146106bf5780637a88bdbd146106d45780637e09b9c2146106e95761038c565b80633e413bee116102c157806351f1985c1161025f57806364ec4e5c1161022e57806364ec4e5c1461066b57806368e2a014146106805780636cbc4a6e1461069557806375a8b012146106aa5761038c565b806351f1985c146106175780635aa8fb481461062c5780635ae51b82146106415780635c0cb479146106565761038c565b8063423d03f91161029b578063423d03f9146105c357806344211d62146105d85780634a7101d5146105ed5780634b57b0be146106025761038c565b80633e413bee1461058457806340ab7b8c146105995780634226a9b9146105ae5761038c565b806322320c981161032e5780632f48ab7d116103085780632f48ab7d1461053057806334b4dabb14610545578063372a26cb1461055a5780633ca5b2341461056f5761038c565b806322320c98146104f15780632d3b5207146105065780632e707bd21461051b5761038c565b80631388b4201161036a5780631388b4201461049d57806313989140146104b25780632113240d146104c757806321a360f5146104dc5761038c565b806305d8aa0a1461039b578063085e2c5b146103c257806312dea1601461046c575b3332141561039957600080fd5b005b3480156103a757600080fd5b506103b06109ea565b60408051918252519081900360200190f35b3480156103ce57600080fd5b50610411600480360360a08110156103e557600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608001356109f1565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b8381101561045757818101518382015260200161043f565b50505050905001935050505060405180910390f35b34801561047857600080fd5b50610481610b3d565b604080516001600160a01b039092168252519081900360200190f35b3480156104a957600080fd5b50610481610b55565b3480156104be57600080fd5b506103b0610b6d565b3480156104d357600080fd5b506103b0610b73565b3480156104e857600080fd5b506103b0610b79565b3480156104fd57600080fd5b50610481610b82565b34801561051257600080fd5b506103b0610b9a565b34801561052757600080fd5b506103b0610ba3565b34801561053c57600080fd5b50610481610ba8565b34801561055157600080fd5b506103b0610bc0565b34801561056657600080fd5b50610481610bc5565b34801561057b57600080fd5b50610481610bdd565b34801561059057600080fd5b50610481610bf5565b3480156105a557600080fd5b50610481610c07565b3480156105ba57600080fd5b506103b0610c1f565b3480156105cf57600080fd5b50610481610c27565b3480156105e457600080fd5b506103b0610c3f565b3480156105f957600080fd5b506103b0610c44565b34801561060e57600080fd5b50610481610c49565b34801561062357600080fd5b50610481610c61565b34801561063857600080fd5b506103b0610c79565b34801561064d57600080fd5b506103b0610c7f565b34801561066257600080fd5b506103b0610c85565b34801561067757600080fd5b506103b0610c8a565b34801561068c57600080fd5b506103b0610c91565b3480156106a157600080fd5b506103b0610c98565b3480156106b657600080fd5b506103b0610c9f565b3480156106cb57600080fd5b50610481610ca5565b3480156106e057600080fd5b506103b0610cb8565b3480156106f557600080fd5b506103b0610cbd565b34801561070a57600080fd5b50610481610cc4565b34801561071f57600080fd5b50610481610cdc565b34801561073457600080fd5b506103b0610cf4565b34801561074957600080fd5b50610481610cfc565b34801561075e57600080fd5b506103b0610d14565b34801561077357600080fd5b50610481610d1c565b34801561078857600080fd5b50610481610d34565b34801561079d57600080fd5b506103b0610d4c565b3480156107b257600080fd5b506103b0610d52565b3480156107c757600080fd5b50610481610d5b565b3480156107dc57600080fd5b506103b0610d73565b3480156107f157600080fd5b506103b0610d78565b34801561080657600080fd5b50610481610d7e565b34801561081b57600080fd5b506103b0610d96565b34801561083057600080fd5b506103b0610d9d565b34801561084557600080fd5b506103b0610da4565b34801561085a57600080fd5b50610481610da9565b34801561086f57600080fd5b506103b0610dc1565b34801561088457600080fd5b506103b0610dc9565b34801561089957600080fd5b50610481610dd0565b3480156108ae57600080fd5b50610481610de8565b3480156108c357600080fd5b506103b0610e00565b610399600480360360c08110156108e257600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a08101608082013564010000000081111561092257600080fd5b82018360208201111561093457600080fd5b8035906020019184602083028401116401000000008311171561095657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610e06915050565b3480156109a257600080fd5b50610481611028565b3480156109b757600080fd5b506103b061103a565b3480156109cc57600080fd5b5061048161103f565b3480156109e157600080fd5b50610481611057565b6220000081565b600080546040805163085e2c5b60e01b81526001600160a01b03898116600483015288811660248301526044820188905260648201879052608482018690529151606093929092169163085e2c5b9160a4808201928792909190829003018186803b158015610a5f57600080fd5b505afa158015610a73573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610a9c57600080fd5b815160208301805160405192949293830192919084640100000000821115610ac357600080fd5b908301906020820185811115610ad857600080fd5b8251866020820283011164010000000082111715610af557600080fd5b82525081516020918201928201910280838360005b83811015610b22578181015183820152602001610b0a565b50505050905001604052505050915091509550959350505050565b7352ae12abe5d8bd778bd5397f99ca900624cfadd481565b73794e6e91555438afc3ccf1c5076a74f42133d08d81565b61200081565b61800081565b64020000000081565b73a5407eae9ba41422680e2e00537571bcc53efbfd81565b64010000000081565b608081565b73dac17f958d2ee523a2206206994597c13d831ec781565b604081565b7379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2781565b734fabb145d64652a948d72533023f6e7a623c7c5381565b600080516020613ec183398151915281565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c81565b630100000081565b7345f783cce6b7ff23b2ab2d70e416cdb7d6055f5181565b601081565b602081565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b73a2b47e3d5c44877cca798226b7b8118f9bfb7a5681565b61400081565b61080081565b600881565b6202000081565b6210000081565b6208000081565b61040081565b6e085d4780b73119b644ae5ecd22b37681565b600281565b6240000081565b73398ec7346dcd622edc5ae82352f02be94c62d11981565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce31581565b630400000081565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9581565b630800000081565b734ddc2d193948926d02f9b1fe9e1daa0718270ed581565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b7306af07097c9eeb7fd685c692751d5c66db49c21581565b600181565b61020081565b7357ab1ec28d129707052df4df418d58a2d46d5f5181565b6280000081565b6204000081565b600c81565b7352ea46506b9cc5ef470c5bf89f17dc28bb35d85c81565b630200000081565b6201000081565b73f6e2d7f616b67e46d708e4410746e9aab3a4c51881565b73818e6fecd516ecc3849daf6845e3ec868087b75581565b61010081565b846001600160a01b0316866001600160a01b03161415610e2557611020565b610e2d613e25565b60405180610180016040528061106681526020016112e78152602001611472815260200161148f815260200161176881526020016118f38152602001611ac48152602001611ce98152602001611f18815260200161214781526020016122e581526020016124918152509050600c83511115610eda5760405162461bcd60e51b8152600401808060200182810382526042815260200180613f416042913960600191505060405180910390fd5b600080805b8551811015610f38576000868281518110610ef657fe5b60200260200101511115610f3057610f2a868281518110610f1357fe5b6020026020010151846125fd90919063ffffffff16565b92508091505b600101610edf565b5060008211610f785760405162461bcd60e51b815260040180806020018281038252602f815260200180613e71602f913960400191505060405180910390fd5b8660005b865181101561101a57868181518110610f9157fe5b602002602001015160001415610fa657611012565b6000610fde85610fd28a8581518110610fbb57fe5b60200260200101518d61266090919063ffffffff16565b9063ffffffff6126b916565b905083821415610feb5750815b808303925061100f8c8c838986600c811061100257fe5b602002015163ffffffff16565b50505b600101610f7c565b50505050505b505050505050565b600080516020613e5183398151915281565b600481565b733d9819210a31b4961b30ef54be2aed79b9c9cd3b81565b6000546001600160a01b031681565b60008161107b6001600160a01b0386166126fb565b6111ab57604080516303795fb160e11b81526001600160a01b0387166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156110dd57600080fd5b505afa1580156110f1573d6000803e3d6000fd5b505050506040513d602081101561110757600080fd5b505190506001600160a01b038116156111a9576111248682612737565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561117a57600080fd5b505af115801561118e573d6000803e3d6000fd5b505050506040513d60208110156111a457600080fd5b505191505b505b6111bd846001600160a01b03166126fb565b6112dd57604080516303795fb160e11b81526001600160a01b0386166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b15801561121f57600080fd5b505afa158015611233573d6000803e3d6000fd5b505050506040513d602081101561124957600080fd5b505190506001600160a01b038116156112db57806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b1580156112ab57600080fd5b505af11580156112bf573d6000803e3d6000fd5b50505050506040513d60208110156112d657600080fd5b505191505b505b90505b9392505050565b60006113078473818e6fecd516ecc3849daf6845e3ec868087b755612737565b73818e6fecd516ecc3849daf6845e3ec868087b7556329589f616113336001600160a01b0387166126fb565b61133e576000611340565b835b611352876001600160a01b03166126fb565b61135c5786611372565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b85611385886001600160a01b03166126fb565b61138f57876113a5565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b15801561143d57600080fd5b505af1158015611451573d6000803e3d6000fd5b50505050506040513d602081101561146857600080fd5b5051949350505050565b6000806114808585856127f0565b9050600081116112dd57600080fd5b60006114a3846001600160a01b03166126fb565b156115115773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156114f757600080fd5b505af115801561150b573d6000803e3d6000fd5b50505050505b611560611526856001600160a01b03166126fb565b6115305784611546565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b73794e6e91555438afc3ccf1c5076a74f42133d08d612737565b600073794e6e91555438afc3ccf1c5076a74f42133d08d630621b4f661158e6001600160a01b0388166126fb565b61159857866115ae565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b856115c1886001600160a01b03166126fb565b6115cb57876115e1565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b15801561163f57600080fd5b505af1158015611653573d6000803e3d6000fd5b505050506040513d602081101561166957600080fd5b5051905061167f6001600160a01b0385166126fb565b156112dd57604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b1580156116dd57600080fd5b505afa1580156116f1573d6000803e3d6000fd5b505050506040513d602081101561170757600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561174757600080fd5b505af115801561175b573d6000803e3d6000fd5b5050505090509392505050565b6000806001600160a01b038516600080516020613ec183398151915214611790576000611793565b60025b6001600160a01b038616600080516020613e51833981519152146117b85760006117bb565b60015b0160ff1690506000600080516020613ec18339815191526001600160a01b038616146117e85760006117eb565b60025b6001600160a01b038616600080516020613e5183398151915214611810576000611813565b60015b0160ff16905081600f0b6000148061182e575080600f0b6000145b1561183e576000925050506112e0565b61185c8673a2b47e3d5c44877cca798226b7b8118f9bfb7a56612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a2b47e3d5c44877cca798226b7b8118f9bfb7a569263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b505af11580156118e6573d6000803e3d6000fd5b5050505050509392505050565b6000806001600160a01b03851673dac17f958d2ee523a2206206994597c13d831ec714611921576000611924565b60035b6001600160a01b038616600080516020613ec18339815191521461194957600061194c565b60025b6001600160a01b038716600080516020613e5183398151915214611971576000611974565b60015b010160ff169050600073dac17f958d2ee523a2206206994597c13d831ec76001600160a01b0316856001600160a01b0316146119b15760006119b4565b60035b6001600160a01b038616600080516020613ec1833981519152146119d95760006119dc565b60025b6001600160a01b038716600080516020613e5183398151915214611a01576000611a04565b60015b010160ff16905081600f0b60001480611a20575080600f0b6000145b15611a30576000925050506112e0565b611a4e867352ea46506b9cc5ef470c5bf89f17dc28bb35d85c612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517352ea46506b9cc5ef470c5bf89f17dc28bb35d85c9263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b6000806001600160a01b0385166e085d4780b73119b644ae5ecd22b37614611aed576000611af0565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611b1b576000611b1e565b60035b6001600160a01b038716600080516020613ec183398151915214611b43576000611b46565b60025b6001600160a01b038816600080516020613e5183398151915214611b6b576000611b6e565b60015b01010160ff16905060006e085d4780b73119b644ae5ecd22b3766001600160a01b0316856001600160a01b031614611ba7576000611baa565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611bd5576000611bd8565b60035b6001600160a01b038716600080516020613ec183398151915214611bfd576000611c00565b60025b6001600160a01b038816600080516020613e5183398151915214611c25576000611c28565b60015b01010160ff16905081600f0b60001480611c45575080600f0b6000145b15611c55576000925050506112e0565b611c73867345f783cce6b7ff23b2ab2d70e416cdb7d6055f51612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517345f783cce6b7ff23b2ab2d70e416cdb7d6055f519263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b6000806001600160a01b038516734fabb145d64652a948d72533023f6e7a623c7c5314611d17576000611d1a565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611d45576000611d48565b60035b6001600160a01b038716600080516020613ec183398151915214611d6d576000611d70565b60025b6001600160a01b038816600080516020613e5183398151915214611d95576000611d98565b60015b01010160ff1690506000734fabb145d64652a948d72533023f6e7a623c7c536001600160a01b0316856001600160a01b031614611dd6576000611dd9565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611e04576000611e07565b60035b6001600160a01b038716600080516020613ec183398151915214611e2c576000611e2f565b60025b6001600160a01b038816600080516020613e5183398151915214611e54576000611e57565b60015b01010160ff16905081600f0b60001480611e74575080600f0b6000145b15611e84576000925050506112e0565b611ea2867379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517379a8c46dea5ada233abaffd40f3a0a2b1e5a4f279263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b6000806001600160a01b0385167357ab1ec28d129707052df4df418d58a2d46d5f5114611f46576000611f49565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611f74576000611f77565b60035b6001600160a01b038716600080516020613ec183398151915214611f9c576000611f9f565b60025b6001600160a01b038816600080516020613e5183398151915214611fc4576000611fc7565b60015b01010160ff16905060007357ab1ec28d129707052df4df418d58a2d46d5f516001600160a01b0316856001600160a01b031614612005576000612008565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714612033576000612036565b60035b6001600160a01b038716600080516020613ec18339815191521461205b57600061205e565b60025b6001600160a01b038816600080516020613e5183398151915214612083576000612086565b60015b01010160ff16905081600f0b600014806120a3575080600f0b6000145b156120b3576000925050506112e0565b6120d18673a5407eae9ba41422680e2e00537571bcc53efbfd612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a5407eae9ba41422680e2e00537571bcc53efbfd9263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b600061215b846001600160a01b03166126fb565b61221457600061216a85612bde565b90506121768582612737565b806001600160a01b031663a0712d68846040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156121bc57600080fd5b505af11580156121d0573d6000803e3d6000fd5b505050506040513d60208110156121e657600080fd5b5061220c905081856122076001600160a01b0383163063ffffffff612dce16565b611066565b9150506112e0565b612226836001600160a01b03166126fb565b6122db57600061223584612bde565b90506000612244868386611066565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561228c57600080fd5b505af11580156122a0573d6000803e3d6000fd5b505050506040513d60208110156122b657600080fd5b506122d290506001600160a01b0386163063ffffffff612dce16565b925050506112e0565b5060009392505050565b60006001600160a01b038416600080516020613e5183398151915214156123c857612324847306af07097c9eeb7fd685c692751d5c66db49c215612737565b60408051633b4da69f60e01b81523060048201526024810184905290517306af07097c9eeb7fd685c692751d5c66db49c21591633b4da69f91604480830192600092919082900301818387803b15801561237d57600080fd5b505af1158015612391573d6000803e3d6000fd5b506123c192507306af07097c9eeb7fd685c692751d5c66db49c2159150859050612207823063ffffffff612dce16565b90506112e0565b6001600160a01b038316600080516020613e5183398151915214156122db576000612408857306af07097c9eeb7fd685c692751d5c66db49c21585611066565b6040805163ef693bed60e01b81523060048201526024810183905290519192507306af07097c9eeb7fd685c692751d5c66db49c2159163ef693bed9160448082019260009290919082900301818387803b15801561246557600080fd5b505af1158015612479573d6000803e3d6000fd5b5061220c925050506001600160a01b03851630612dce565b60006124a5846001600160a01b03166126fb565b6125635760006124b485612e78565b90506124c08582612737565b60408051636968703360e11b81526001600160a01b03871660048201526024810185905261044d6044820152905173398ec7346dcd622edc5ae82352f02be94c62d1199163d2d0e06691606480830192600092919082900301818387803b15801561252a57600080fd5b505af115801561253e573d6000803e3d6000fd5b5050505061220c818561220730856001600160a01b0316612dce90919063ffffffff16565b612575836001600160a01b03166126fb565b6122db57600061258484612e78565b90506000612593868386611066565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156125db57600080fd5b505af11580156125ef573d6000803e3d6000fd5b5050505080925050506112e0565b600082820183811015612657576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60008261266f5750600061265a565b8282028284828161267c57fe5b04146126575760405162461bcd60e51b8152600401808060200182810382526021815260200180613ea06021913960400191505060405180910390fd5b600061265783836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061329b565b60006001600160a01b038216158061272f57506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b90505b919050565b612749826001600160a01b03166126fb565b6127ec5760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b15801561279e57600080fd5b505afa1580156127b2573d6000803e3d6000fd5b505050506040513d60208110156127c857600080fd5b5051901c6127ec576127ec6001600160a01b0383168260001963ffffffff61333d16565b5050565b6000612804846001600160a01b03166126fb565b156128725773c0829421c1d260bd3cb3e0f06cfe2d52db2ce3156001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561285857600080fd5b505af115801561286c573d6000803e3d6000fd5b50505050505b60007352ae12abe5d8bd778bd5397f99ca900624cfadd46001600160a01b031663bb34534c6040518163ffffffff1660e01b815260040180806c42616e636f724e6574776f726b60981b815250602001905060206040518083038186803b1580156128dc57600080fd5b505afa1580156128f0573d6000803e3d6000fd5b505050506040513d602081101561290657600080fd5b5051905060606129168686613413565b905061295361292d876001600160a01b03166126fb565b612937578661294d565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b83612737565b60006060836001600160a01b03166216e360856001600160a01b031663c7ba24bc905060e01b8589600160405160240180806020018481526020018360ff168152602001828103825285818151815260200191508051906020019060200280838360005b838110156129cf5781810151838201526020016129b7565b50505050905001945050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0383818316178352505050506040518082805190602001908083835b60208310612a3e5780518252601f199092019160209182019101612a1f565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038160008787f1925050503d8060008114612aa1576040519150601f19603f3d011682016040523d82523d6000602084013e612aa6565b606091505b5091509150600082612ab9576000612ad1565b818060200190516020811015612ace57600080fd5b50515b9050612ae5886001600160a01b03166126fb565b8015612af15750600081115b15612bd257604080516370a0823160e01b8152306004820152905173c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591632e1a7d4d9183916370a08231916024808301926020929190829003018186803b158015612b4f57600080fd5b505afa158015612b63573d6000803e3d6000fd5b505050506040513d6020811015612b7957600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015612bb957600080fd5b505af1158015612bcd573d6000803e3d6000fd5b505050505b98975050505050505050565b6000612bf2826001600160a01b03166126fb565b15612c125750734ddc2d193948926d02f9b1fe9e1daa0718270ed5612732565b6001600160a01b038216600080516020613e518339815191521415612c4c5750735d3a536e4d6dbd6114cc1ead35777bab948e3643612732565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415612c8c5750736c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e612732565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e8621415612ccc575073158079ee67fce2f58472a96584a73c7ab9ac95c1612732565b6001600160a01b038216600080516020613ec18339815191521415612d0657507339aa39c021dfbae8fac545936693ac917d5e7563612732565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415612d46575073c11b1268c1a384e55c48c2391d8d480264a3a7f4612732565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415612d86575073b3319f5d18bc0d84dd1b4825dcde5d5f7266d407612732565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec71415612dc6575073f650c3d88d12db855b8bf7d11be6c55a4e07dcc9612732565b506000919050565b6000612dd9836126fb565b15612def57506001600160a01b0381163161265a565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015612e4557600080fd5b505afa158015612e59573d6000803e3d6000fd5b505050506040513d6020811015612e6f57600080fd5b5051905061265a565b6000612e8c826001600160a01b03166126fb565b15612eac5750733a3a65aab0dd2a17e3f1947ba16138cd37d08c04612732565b6001600160a01b038216600080516020613e518339815191521415612ee6575073fc1e690f61efd961294b3e1ce3313fbd8aa4f85d612732565b6001600160a01b038216600080516020613ec18339815191521415612f205750739ba00d6856a4edf4665bca2c2309936572473b7e612732565b6001600160a01b0382167357ab1ec28d129707052df4df418d58a2d46d5f511415612f60575073625ae63000f46200499120b906716420bd059240612732565b6001600160a01b038216734fabb145d64652a948d72533023f6e7a623c7c531415612fa05750736ee0f7bb50a54ab5253da0667b0dc2ee526c30a8612732565b6001600160a01b0382166e085d4780b73119b644ae5ecd22b3761415612fdb5750734da9b813057d04baef4e5800e36083717b4a0341612732565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec7141561301b57507371fc860f7d3a592a4a98740e39db31d25db65ae8612732565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef141561305b575073e1ba0fb44ccb0d11b80f92f4f8ed94ca3ff51d00612732565b6001600160a01b03821673dd974d5c2e2928dea5f71b9825b8b646686bd200141561309b5750739d91be44c06d373a8a226e1f3b146956083803eb612732565b6001600160a01b0382167380fb784b7ed66730e8b1dbd9820afd29931aab0314156130db5750737d2d3688df45ce7c552e19c27e007673da9204b8612732565b6001600160a01b03821673514910771af9ca656af840dff83e8264ecf986ca141561311b575073a64bd6c70cb9051f6a9ba1f163fdc07e0dfb5f84612732565b6001600160a01b038216730f5d2fb29fb7d3cfee444a200298f468908cc942141561315b5750736fce4a401b6b80ace52baaefe4421bd188e76f6f612732565b6001600160a01b038216739f8f72aa9304c8b593d555f12ef6589cc3a579a2141561319b5750737deb5e830be29f91e298ba5ff1356bb7f8146998612732565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e86214156131db57507371010a9d003445ac60c4e6a7017c1e89a477b438612732565b6001600160a01b03821673c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f141561321b575073328c4c80bc7aca0834db37e6600a6c49e12da4de612732565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c599141561325b575073fc4b8ed459e00e5400be803a9bb3954234fd50e3612732565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415612dc65750736fb0855c404e09c47c3fbca25f08d4e41f9f062f612732565b600081836133275760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156132ec5781810151838201526020016132d4565b50505050905090810190601f1680156133195780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161333357fe5b0495945050505050565b613346836126fb565b61340e576000811180156133d4575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b1580156133a657600080fd5b505afa1580156133ba573d6000803e3d6000fd5b505050506040513d60208110156133d057600080fd5b5051115b156133f4576133f46001600160a01b03841683600063ffffffff613b1d16565b61340e6001600160a01b038416838363ffffffff613b1d16565b505050565b6060816001600160a01b0316836001600160a01b03161415613444575060408051600081526020810190915261265a565b613456836001600160a01b03166126fb565b156134735773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31592505b613485826001600160a01b03166126fb565b156134a25773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591505b6001600160a01b038316731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14806134e957506001600160a01b038216731f573d6fb3f13d689ff844b4ce37794d79a7ff1c145b1561351457604080516003808252608082019092529060208201606080388339019050509050613536565b60408051600580825260c08201909252906020820160a0803883390190505090505b6000806001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c146136ff576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6135936001600160a01b038b166126fb565b61359d57896135b3565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106136315780518252601f199092019160209182019101613612565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613692576040519150601f19603f3d011682016040523d82523d6000602084013e613697565b606091505b5091509150816136bf5760408051600080825260208201909252905b5094505050505061265a565b8080602001905160208110156136d457600080fd5b505193506001600160a01b0384166136fc5760408051600080825260208201909252906136b3565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c146138bd576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6137596001600160a01b038a166126fb565b6137635788613779565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106137f75780518252601f1990920191602091820191016137d8565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613858576040519150601f19603f3d011682016040523d82523d6000602084013e61385d565b606091505b50915091508161387d5760408051600080825260208201909252906136b3565b80806020019051602081101561389257600080fd5b505192506001600160a01b0383166138ba5760408051600080825260208201909252906136b3565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156139805784836000815181106138f057fe5b60200260200101906001600160a01b031690816001600160a01b031681525050818360018151811061391e57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360028151811061396057fe5b6001600160a01b03909216602092830291909101909101525061265a9050565b6001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c1415613a2357731f573d6fb3f13d689ff844b4ce37794d79a7ff1c836000815181106139c757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505080836001815181106139f557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050838360028151811061396057fe5b8483600081518110613a3157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508183600181518110613a5f57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c83600281518110613aa157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508083600381518110613acf57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508383600481518110613afd57fe5b6001600160a01b0390921660209283029190910190910152505092915050565b801580613ba3575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b158015613b7557600080fd5b505afa158015613b89573d6000803e3d6000fd5b505050506040513d6020811015613b9f57600080fd5b5051155b613bde5760405162461bcd60e51b8152600401808060200182810382526036815260200180613f0b6036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261340e908490613c3d826001600160a01b0316613de9565b613c8e576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310613ccc5780518252601f199092019160209182019101613cad565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613d2e576040519150601f19603f3d011682016040523d82523d6000602084013e613d33565b606091505b509150915081613d8a576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115613de357808060200190516020811015613da657600080fd5b5051613de35760405162461bcd60e51b815260040180806020018281038252602a815260200180613ee1602a913960400191505060405180910390fd5b50505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590613e1d57508115155b949350505050565b604051806101800160405280600c905b613e4e815260200190600190039081613e355790505090565bfefe0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f73536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e63654f6e6553706c69743a20446973747269627574696f6e2061727261792073686f756c64206e6f74206578636565642072657365727665732061727261792073697a65a265627a7a72315820ca5088fe2af0082cd092e41fef403a00ef3cb50b20866f719ac8ea952578659264736f6c63430005110032 \ No newline at end of file +608060405234801561001057600080fd5b5060405161401c38038061401c8339818101604052602081101561003357600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055613fb7806100656000396000f3fe60806040526004361061038c5760003560e01c806375b5be2d116101dc578063c925777511610102578063d70a2d1f116100a0578063f4b9fa751161006f578063f4b9fa7514610996578063f56e281f146109ab578063f69e2046146109c0578063fbe4ed95146109d55761038c565b8063d70a2d1f1461088d578063d77366a4146108a2578063dc1536b2146108b7578063e2a7515e146108cc5761038c565b8063cc26e9fc116100dc578063cc26e9fc14610839578063cede5f6a1461084e578063d1aee5e314610863578063d393c3e9146108785761038c565b8063c9257775146107fa578063c989b6671461080f578063c9b42c67146108245761038c565b80638ea812c01161017a578063b3bc784411610149578063b3bc7844146107a6578063b69d0456146107bb578063c762a46c146107d0578063c77b9de6146107e55761038c565b80638ea812c014610752578063a1b4d01114610767578063a734f06e1461077c578063b0a7ef29146107915761038c565b8063819faf7b116101b6578063819faf7b146106fe578063851954fa146107135780638aea49d2146107285780638bdb2afa1461073d5761038c565b806375b5be2d146106bf5780637a88bdbd146106d45780637e09b9c2146106e95761038c565b80633e413bee116102c157806351f1985c1161025f57806364ec4e5c1161022e57806364ec4e5c1461066b57806368e2a014146106805780636cbc4a6e1461069557806375a8b012146106aa5761038c565b806351f1985c146106175780635aa8fb481461062c5780635ae51b82146106415780635c0cb479146106565761038c565b8063423d03f91161029b578063423d03f9146105c357806344211d62146105d85780634a7101d5146105ed5780634b57b0be146106025761038c565b80633e413bee1461058457806340ab7b8c146105995780634226a9b9146105ae5761038c565b806322320c981161032e5780632f48ab7d116103085780632f48ab7d1461053057806334b4dabb14610545578063372a26cb1461055a5780633ca5b2341461056f5761038c565b806322320c98146104f15780632d3b5207146105065780632e707bd21461051b5761038c565b80631388b4201161036a5780631388b4201461049d57806313989140146104b25780632113240d146104c757806321a360f5146104dc5761038c565b806305d8aa0a1461039b578063085e2c5b146103c257806312dea1601461046c575b3332141561039957600080fd5b005b3480156103a757600080fd5b506103b06109ea565b60408051918252519081900360200190f35b3480156103ce57600080fd5b50610411600480360360a08110156103e557600080fd5b506001600160a01b038135811691602081013590911690604081013590606081013590608001356109f1565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b8381101561045757818101518382015260200161043f565b50505050905001935050505060405180910390f35b34801561047857600080fd5b50610481610b3d565b604080516001600160a01b039092168252519081900360200190f35b3480156104a957600080fd5b50610481610b55565b3480156104be57600080fd5b506103b0610b6d565b3480156104d357600080fd5b506103b0610b73565b3480156104e857600080fd5b506103b0610b79565b3480156104fd57600080fd5b50610481610b82565b34801561051257600080fd5b506103b0610b9a565b34801561052757600080fd5b506103b0610ba3565b34801561053c57600080fd5b50610481610ba8565b34801561055157600080fd5b506103b0610bc0565b34801561056657600080fd5b50610481610bc5565b34801561057b57600080fd5b50610481610bdd565b34801561059057600080fd5b50610481610bf5565b3480156105a557600080fd5b50610481610c07565b3480156105ba57600080fd5b506103b0610c1f565b3480156105cf57600080fd5b50610481610c27565b3480156105e457600080fd5b506103b0610c3f565b3480156105f957600080fd5b506103b0610c44565b34801561060e57600080fd5b50610481610c49565b34801561062357600080fd5b50610481610c61565b34801561063857600080fd5b506103b0610c79565b34801561064d57600080fd5b506103b0610c7f565b34801561066257600080fd5b506103b0610c85565b34801561067757600080fd5b506103b0610c8a565b34801561068c57600080fd5b506103b0610c91565b3480156106a157600080fd5b506103b0610c98565b3480156106b657600080fd5b506103b0610c9f565b3480156106cb57600080fd5b50610481610ca5565b3480156106e057600080fd5b506103b0610cb8565b3480156106f557600080fd5b506103b0610cbd565b34801561070a57600080fd5b50610481610cc4565b34801561071f57600080fd5b50610481610cdc565b34801561073457600080fd5b506103b0610cf4565b34801561074957600080fd5b50610481610cfc565b34801561075e57600080fd5b506103b0610d14565b34801561077357600080fd5b50610481610d1c565b34801561078857600080fd5b50610481610d34565b34801561079d57600080fd5b506103b0610d4c565b3480156107b257600080fd5b506103b0610d52565b3480156107c757600080fd5b50610481610d5b565b3480156107dc57600080fd5b506103b0610d73565b3480156107f157600080fd5b506103b0610d78565b34801561080657600080fd5b50610481610d7e565b34801561081b57600080fd5b506103b0610d96565b34801561083057600080fd5b506103b0610d9d565b34801561084557600080fd5b506103b0610da4565b34801561085a57600080fd5b50610481610da9565b34801561086f57600080fd5b506103b0610dc1565b34801561088457600080fd5b506103b0610dc9565b34801561089957600080fd5b50610481610dd0565b3480156108ae57600080fd5b50610481610de8565b3480156108c357600080fd5b506103b0610e00565b610399600480360360c08110156108e257600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a08101608082013564010000000081111561092257600080fd5b82018360208201111561093457600080fd5b8035906020019184602083028401116401000000008311171561095657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610e06915050565b3480156109a257600080fd5b50610481611028565b3480156109b757600080fd5b506103b061103a565b3480156109cc57600080fd5b5061048161103f565b3480156109e157600080fd5b50610481611057565b6220000081565b600080546040805163085e2c5b60e01b81526001600160a01b03898116600483015288811660248301526044820188905260648201879052608482018690529151606093929092169163085e2c5b9160a4808201928792909190829003018186803b158015610a5f57600080fd5b505afa158015610a73573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610a9c57600080fd5b815160208301805160405192949293830192919084640100000000821115610ac357600080fd5b908301906020820185811115610ad857600080fd5b8251866020820283011164010000000082111715610af557600080fd5b82525081516020918201928201910280838360005b83811015610b22578181015183820152602001610b0a565b50505050905001604052505050915091509550959350505050565b7352ae12abe5d8bd778bd5397f99ca900624cfadd481565b73794e6e91555438afc3ccf1c5076a74f42133d08d81565b61200081565b61800081565b64020000000081565b73a5407eae9ba41422680e2e00537571bcc53efbfd81565b64010000000081565b608081565b73dac17f958d2ee523a2206206994597c13d831ec781565b604081565b7379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2781565b734fabb145d64652a948d72533023f6e7a623c7c5381565b600080516020613ec183398151915281565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c81565b630100000081565b7345f783cce6b7ff23b2ab2d70e416cdb7d6055f5181565b601081565b602081565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b73a2b47e3d5c44877cca798226b7b8118f9bfb7a5681565b61400081565b61080081565b600881565b6202000081565b6210000081565b6208000081565b61040081565b6e085d4780b73119b644ae5ecd22b37681565b600281565b6240000081565b73398ec7346dcd622edc5ae82352f02be94c62d11981565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce31581565b630400000081565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9581565b630800000081565b734ddc2d193948926d02f9b1fe9e1daa0718270ed581565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b64040000000081565b7306af07097c9eeb7fd685c692751d5c66db49c21581565b600181565b61020081565b7357ab1ec28d129707052df4df418d58a2d46d5f5181565b6280000081565b6204000081565b600c81565b7352ea46506b9cc5ef470c5bf89f17dc28bb35d85c81565b630200000081565b6201000081565b73f6e2d7f616b67e46d708e4410746e9aab3a4c51881565b73818e6fecd516ecc3849daf6845e3ec868087b75581565b61010081565b846001600160a01b0316866001600160a01b03161415610e2557611020565b610e2d613e25565b60405180610180016040528061106681526020016112e78152602001611472815260200161148f815260200161176881526020016118f38152602001611ac48152602001611ce98152602001611f18815260200161214781526020016122e581526020016124918152509050600c83511115610eda5760405162461bcd60e51b8152600401808060200182810382526042815260200180613f416042913960600191505060405180910390fd5b600080805b8551811015610f38576000868281518110610ef657fe5b60200260200101511115610f3057610f2a868281518110610f1357fe5b6020026020010151846125fd90919063ffffffff16565b92508091505b600101610edf565b5060008211610f785760405162461bcd60e51b815260040180806020018281038252602f815260200180613e71602f913960400191505060405180910390fd5b8660005b865181101561101a57868181518110610f9157fe5b602002602001015160001415610fa657611012565b6000610fde85610fd28a8581518110610fbb57fe5b60200260200101518d61266090919063ffffffff16565b9063ffffffff6126b916565b905083821415610feb5750815b808303925061100f8c8c838986600c811061100257fe5b602002015163ffffffff16565b50505b600101610f7c565b50505050505b505050505050565b600080516020613e5183398151915281565b600481565b733d9819210a31b4961b30ef54be2aed79b9c9cd3b81565b6000546001600160a01b031681565b60008161107b6001600160a01b0386166126fb565b6111ab57604080516303795fb160e11b81526001600160a01b0387166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156110dd57600080fd5b505afa1580156110f1573d6000803e3d6000fd5b505050506040513d602081101561110757600080fd5b505190506001600160a01b038116156111a9576111248682612737565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561117a57600080fd5b505af115801561118e573d6000803e3d6000fd5b505050506040513d60208110156111a457600080fd5b505191505b505b6111bd846001600160a01b03166126fb565b6112dd57604080516303795fb160e11b81526001600160a01b0386166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b15801561121f57600080fd5b505afa158015611233573d6000803e3d6000fd5b505050506040513d602081101561124957600080fd5b505190506001600160a01b038116156112db57806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b1580156112ab57600080fd5b505af11580156112bf573d6000803e3d6000fd5b50505050506040513d60208110156112d657600080fd5b505191505b505b90505b9392505050565b60006113078473818e6fecd516ecc3849daf6845e3ec868087b755612737565b73818e6fecd516ecc3849daf6845e3ec868087b7556329589f616113336001600160a01b0387166126fb565b61133e576000611340565b835b611352876001600160a01b03166126fb565b61135c5786611372565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b85611385886001600160a01b03166126fb565b61138f57876113a5565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b15801561143d57600080fd5b505af1158015611451573d6000803e3d6000fd5b50505050506040513d602081101561146857600080fd5b5051949350505050565b6000806114808585856127f0565b9050600081116112dd57600080fd5b60006114a3846001600160a01b03166126fb565b156115115773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156114f757600080fd5b505af115801561150b573d6000803e3d6000fd5b50505050505b611560611526856001600160a01b03166126fb565b6115305784611546565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b73794e6e91555438afc3ccf1c5076a74f42133d08d612737565b600073794e6e91555438afc3ccf1c5076a74f42133d08d630621b4f661158e6001600160a01b0388166126fb565b61159857866115ae565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b856115c1886001600160a01b03166126fb565b6115cb57876115e1565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b15801561163f57600080fd5b505af1158015611653573d6000803e3d6000fd5b505050506040513d602081101561166957600080fd5b5051905061167f6001600160a01b0385166126fb565b156112dd57604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b1580156116dd57600080fd5b505afa1580156116f1573d6000803e3d6000fd5b505050506040513d602081101561170757600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561174757600080fd5b505af115801561175b573d6000803e3d6000fd5b5050505090509392505050565b6000806001600160a01b038516600080516020613ec183398151915214611790576000611793565b60025b6001600160a01b038616600080516020613e51833981519152146117b85760006117bb565b60015b0160ff1690506000600080516020613ec18339815191526001600160a01b038616146117e85760006117eb565b60025b6001600160a01b038616600080516020613e5183398151915214611810576000611813565b60015b0160ff16905081600f0b6000148061182e575080600f0b6000145b1561183e576000925050506112e0565b61185c8673a2b47e3d5c44877cca798226b7b8118f9bfb7a56612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a2b47e3d5c44877cca798226b7b8118f9bfb7a569263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b505af11580156118e6573d6000803e3d6000fd5b5050505050509392505050565b6000806001600160a01b03851673dac17f958d2ee523a2206206994597c13d831ec714611921576000611924565b60035b6001600160a01b038616600080516020613ec18339815191521461194957600061194c565b60025b6001600160a01b038716600080516020613e5183398151915214611971576000611974565b60015b010160ff169050600073dac17f958d2ee523a2206206994597c13d831ec76001600160a01b0316856001600160a01b0316146119b15760006119b4565b60035b6001600160a01b038616600080516020613ec1833981519152146119d95760006119dc565b60025b6001600160a01b038716600080516020613e5183398151915214611a01576000611a04565b60015b010160ff16905081600f0b60001480611a20575080600f0b6000145b15611a30576000925050506112e0565b611a4e867352ea46506b9cc5ef470c5bf89f17dc28bb35d85c612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517352ea46506b9cc5ef470c5bf89f17dc28bb35d85c9263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b6000806001600160a01b0385166e085d4780b73119b644ae5ecd22b37614611aed576000611af0565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611b1b576000611b1e565b60035b6001600160a01b038716600080516020613ec183398151915214611b43576000611b46565b60025b6001600160a01b038816600080516020613e5183398151915214611b6b576000611b6e565b60015b01010160ff16905060006e085d4780b73119b644ae5ecd22b3766001600160a01b0316856001600160a01b031614611ba7576000611baa565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611bd5576000611bd8565b60035b6001600160a01b038716600080516020613ec183398151915214611bfd576000611c00565b60025b6001600160a01b038816600080516020613e5183398151915214611c25576000611c28565b60015b01010160ff16905081600f0b60001480611c45575080600f0b6000145b15611c55576000925050506112e0565b611c73867345f783cce6b7ff23b2ab2d70e416cdb7d6055f51612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517345f783cce6b7ff23b2ab2d70e416cdb7d6055f519263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b6000806001600160a01b038516734fabb145d64652a948d72533023f6e7a623c7c5314611d17576000611d1a565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611d45576000611d48565b60035b6001600160a01b038716600080516020613ec183398151915214611d6d576000611d70565b60025b6001600160a01b038816600080516020613e5183398151915214611d95576000611d98565b60015b01010160ff1690506000734fabb145d64652a948d72533023f6e7a623c7c536001600160a01b0316856001600160a01b031614611dd6576000611dd9565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611e04576000611e07565b60035b6001600160a01b038716600080516020613ec183398151915214611e2c576000611e2f565b60025b6001600160a01b038816600080516020613e5183398151915214611e54576000611e57565b60015b01010160ff16905081600f0b60001480611e74575080600f0b6000145b15611e84576000925050506112e0565b611ea2867379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517379a8c46dea5ada233abaffd40f3a0a2b1e5a4f279263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b6000806001600160a01b0385167357ab1ec28d129707052df4df418d58a2d46d5f5114611f46576000611f49565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714611f74576000611f77565b60035b6001600160a01b038716600080516020613ec183398151915214611f9c576000611f9f565b60025b6001600160a01b038816600080516020613e5183398151915214611fc4576000611fc7565b60015b01010160ff16905060007357ab1ec28d129707052df4df418d58a2d46d5f516001600160a01b0316856001600160a01b031614612005576000612008565b60045b6001600160a01b03861673dac17f958d2ee523a2206206994597c13d831ec714612033576000612036565b60035b6001600160a01b038716600080516020613ec18339815191521461205b57600061205e565b60025b6001600160a01b038816600080516020613e5183398151915214612083576000612086565b60015b01010160ff16905081600f0b600014806120a3575080600f0b6000145b156120b3576000925050506112e0565b6120d18673a5407eae9ba41422680e2e00537571bcc53efbfd612737565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a5407eae9ba41422680e2e00537571bcc53efbfd9263a6417ed6926084808201939182900301818387803b1580156118d257600080fd5b600061215b846001600160a01b03166126fb565b61221457600061216a85612bde565b90506121768582612737565b806001600160a01b031663a0712d68846040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156121bc57600080fd5b505af11580156121d0573d6000803e3d6000fd5b505050506040513d60208110156121e657600080fd5b5061220c905081856122076001600160a01b0383163063ffffffff612dce16565b611066565b9150506112e0565b612226836001600160a01b03166126fb565b6122db57600061223584612bde565b90506000612244868386611066565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561228c57600080fd5b505af11580156122a0573d6000803e3d6000fd5b505050506040513d60208110156122b657600080fd5b506122d290506001600160a01b0386163063ffffffff612dce16565b925050506112e0565b5060009392505050565b60006001600160a01b038416600080516020613e5183398151915214156123c857612324847306af07097c9eeb7fd685c692751d5c66db49c215612737565b60408051633b4da69f60e01b81523060048201526024810184905290517306af07097c9eeb7fd685c692751d5c66db49c21591633b4da69f91604480830192600092919082900301818387803b15801561237d57600080fd5b505af1158015612391573d6000803e3d6000fd5b506123c192507306af07097c9eeb7fd685c692751d5c66db49c2159150859050612207823063ffffffff612dce16565b90506112e0565b6001600160a01b038316600080516020613e5183398151915214156122db576000612408857306af07097c9eeb7fd685c692751d5c66db49c21585611066565b6040805163ef693bed60e01b81523060048201526024810183905290519192507306af07097c9eeb7fd685c692751d5c66db49c2159163ef693bed9160448082019260009290919082900301818387803b15801561246557600080fd5b505af1158015612479573d6000803e3d6000fd5b5061220c925050506001600160a01b03851630612dce565b60006124a5846001600160a01b03166126fb565b6125635760006124b485612e78565b90506124c08582612737565b60408051636968703360e11b81526001600160a01b03871660048201526024810185905261044d6044820152905173398ec7346dcd622edc5ae82352f02be94c62d1199163d2d0e06691606480830192600092919082900301818387803b15801561252a57600080fd5b505af115801561253e573d6000803e3d6000fd5b5050505061220c818561220730856001600160a01b0316612dce90919063ffffffff16565b612575836001600160a01b03166126fb565b6122db57600061258484612e78565b90506000612593868386611066565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156125db57600080fd5b505af11580156125ef573d6000803e3d6000fd5b5050505080925050506112e0565b600082820183811015612657576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b60008261266f5750600061265a565b8282028284828161267c57fe5b04146126575760405162461bcd60e51b8152600401808060200182810382526021815260200180613ea06021913960400191505060405180910390fd5b600061265783836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061329b565b60006001600160a01b038216158061272f57506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b90505b919050565b612749826001600160a01b03166126fb565b6127ec5760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b15801561279e57600080fd5b505afa1580156127b2573d6000803e3d6000fd5b505050506040513d60208110156127c857600080fd5b5051901c6127ec576127ec6001600160a01b0383168260001963ffffffff61333d16565b5050565b6000612804846001600160a01b03166126fb565b156128725773c0829421c1d260bd3cb3e0f06cfe2d52db2ce3156001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b15801561285857600080fd5b505af115801561286c573d6000803e3d6000fd5b50505050505b60007352ae12abe5d8bd778bd5397f99ca900624cfadd46001600160a01b031663bb34534c6040518163ffffffff1660e01b815260040180806c42616e636f724e6574776f726b60981b815250602001905060206040518083038186803b1580156128dc57600080fd5b505afa1580156128f0573d6000803e3d6000fd5b505050506040513d602081101561290657600080fd5b5051905060606129168686613413565b905061295361292d876001600160a01b03166126fb565b612937578661294d565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b83612737565b60006060836001600160a01b03166216e360856001600160a01b031663c7ba24bc905060e01b8589600160405160240180806020018481526020018360ff168152602001828103825285818151815260200191508051906020019060200280838360005b838110156129cf5781810151838201526020016129b7565b50505050905001945050505050604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0383818316178352505050506040518082805190602001908083835b60208310612a3e5780518252601f199092019160209182019101612a1f565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038160008787f1925050503d8060008114612aa1576040519150601f19603f3d011682016040523d82523d6000602084013e612aa6565b606091505b5091509150600082612ab9576000612ad1565b818060200190516020811015612ace57600080fd5b50515b9050612ae5886001600160a01b03166126fb565b8015612af15750600081115b15612bd257604080516370a0823160e01b8152306004820152905173c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591632e1a7d4d9183916370a08231916024808301926020929190829003018186803b158015612b4f57600080fd5b505afa158015612b63573d6000803e3d6000fd5b505050506040513d6020811015612b7957600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015612bb957600080fd5b505af1158015612bcd573d6000803e3d6000fd5b505050505b98975050505050505050565b6000612bf2826001600160a01b03166126fb565b15612c125750734ddc2d193948926d02f9b1fe9e1daa0718270ed5612732565b6001600160a01b038216600080516020613e518339815191521415612c4c5750735d3a536e4d6dbd6114cc1ead35777bab948e3643612732565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415612c8c5750736c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e612732565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e8621415612ccc575073158079ee67fce2f58472a96584a73c7ab9ac95c1612732565b6001600160a01b038216600080516020613ec18339815191521415612d0657507339aa39c021dfbae8fac545936693ac917d5e7563612732565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415612d46575073c11b1268c1a384e55c48c2391d8d480264a3a7f4612732565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415612d86575073b3319f5d18bc0d84dd1b4825dcde5d5f7266d407612732565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec71415612dc6575073f650c3d88d12db855b8bf7d11be6c55a4e07dcc9612732565b506000919050565b6000612dd9836126fb565b15612def57506001600160a01b0381163161265a565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015612e4557600080fd5b505afa158015612e59573d6000803e3d6000fd5b505050506040513d6020811015612e6f57600080fd5b5051905061265a565b6000612e8c826001600160a01b03166126fb565b15612eac5750733a3a65aab0dd2a17e3f1947ba16138cd37d08c04612732565b6001600160a01b038216600080516020613e518339815191521415612ee6575073fc1e690f61efd961294b3e1ce3313fbd8aa4f85d612732565b6001600160a01b038216600080516020613ec18339815191521415612f205750739ba00d6856a4edf4665bca2c2309936572473b7e612732565b6001600160a01b0382167357ab1ec28d129707052df4df418d58a2d46d5f511415612f60575073625ae63000f46200499120b906716420bd059240612732565b6001600160a01b038216734fabb145d64652a948d72533023f6e7a623c7c531415612fa05750736ee0f7bb50a54ab5253da0667b0dc2ee526c30a8612732565b6001600160a01b0382166e085d4780b73119b644ae5ecd22b3761415612fdb5750734da9b813057d04baef4e5800e36083717b4a0341612732565b6001600160a01b03821673dac17f958d2ee523a2206206994597c13d831ec7141561301b57507371fc860f7d3a592a4a98740e39db31d25db65ae8612732565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef141561305b575073e1ba0fb44ccb0d11b80f92f4f8ed94ca3ff51d00612732565b6001600160a01b03821673dd974d5c2e2928dea5f71b9825b8b646686bd200141561309b5750739d91be44c06d373a8a226e1f3b146956083803eb612732565b6001600160a01b0382167380fb784b7ed66730e8b1dbd9820afd29931aab0314156130db5750737d2d3688df45ce7c552e19c27e007673da9204b8612732565b6001600160a01b03821673514910771af9ca656af840dff83e8264ecf986ca141561311b575073a64bd6c70cb9051f6a9ba1f163fdc07e0dfb5f84612732565b6001600160a01b038216730f5d2fb29fb7d3cfee444a200298f468908cc942141561315b5750736fce4a401b6b80ace52baaefe4421bd188e76f6f612732565b6001600160a01b038216739f8f72aa9304c8b593d555f12ef6589cc3a579a2141561319b5750737deb5e830be29f91e298ba5ff1356bb7f8146998612732565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e86214156131db57507371010a9d003445ac60c4e6a7017c1e89a477b438612732565b6001600160a01b03821673c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f141561321b575073328c4c80bc7aca0834db37e6600a6c49e12da4de612732565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c599141561325b575073fc4b8ed459e00e5400be803a9bb3954234fd50e3612732565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415612dc65750736fb0855c404e09c47c3fbca25f08d4e41f9f062f612732565b600081836133275760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156132ec5781810151838201526020016132d4565b50505050905090810190601f1680156133195780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600083858161333357fe5b0495945050505050565b613346836126fb565b61340e576000811180156133d4575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b1580156133a657600080fd5b505afa1580156133ba573d6000803e3d6000fd5b505050506040513d60208110156133d057600080fd5b5051115b156133f4576133f46001600160a01b03841683600063ffffffff613b1d16565b61340e6001600160a01b038416838363ffffffff613b1d16565b505050565b6060816001600160a01b0316836001600160a01b03161415613444575060408051600081526020810190915261265a565b613456836001600160a01b03166126fb565b156134735773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31592505b613485826001600160a01b03166126fb565b156134a25773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591505b6001600160a01b038316731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14806134e957506001600160a01b038216731f573d6fb3f13d689ff844b4ce37794d79a7ff1c145b1561351457604080516003808252608082019092529060208201606080388339019050509050613536565b60408051600580825260c08201909252906020820160a0803883390190505090505b6000806001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c146136ff576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6135936001600160a01b038b166126fb565b61359d57896135b3565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106136315780518252601f199092019160209182019101613612565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613692576040519150601f19603f3d011682016040523d82523d6000602084013e613697565b606091505b5091509150816136bf5760408051600080825260208201909252905b5094505050505061265a565b8080602001905160208110156136d457600080fd5b505193506001600160a01b0384166136fc5760408051600080825260208201909252906136b3565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c146138bd576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6137596001600160a01b038a166126fb565b6137635788613779565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106137f75780518252601f1990920191602091820191016137d8565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613858576040519150601f19603f3d011682016040523d82523d6000602084013e61385d565b606091505b50915091508161387d5760408051600080825260208201909252906136b3565b80806020019051602081101561389257600080fd5b505192506001600160a01b0383166138ba5760408051600080825260208201909252906136b3565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156139805784836000815181106138f057fe5b60200260200101906001600160a01b031690816001600160a01b031681525050818360018151811061391e57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360028151811061396057fe5b6001600160a01b03909216602092830291909101909101525061265a9050565b6001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c1415613a2357731f573d6fb3f13d689ff844b4ce37794d79a7ff1c836000815181106139c757fe5b60200260200101906001600160a01b031690816001600160a01b03168152505080836001815181106139f557fe5b60200260200101906001600160a01b031690816001600160a01b031681525050838360028151811061396057fe5b8483600081518110613a3157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508183600181518110613a5f57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c83600281518110613aa157fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508083600381518110613acf57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508383600481518110613afd57fe5b6001600160a01b0390921660209283029190910190910152505092915050565b801580613ba3575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b158015613b7557600080fd5b505afa158015613b89573d6000803e3d6000fd5b505050506040513d6020811015613b9f57600080fd5b5051155b613bde5760405162461bcd60e51b8152600401808060200182810382526036815260200180613f0b6036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261340e908490613c3d826001600160a01b0316613de9565b613c8e576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b60208310613ccc5780518252601f199092019160209182019101613cad565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613d2e576040519150601f19603f3d011682016040523d82523d6000602084013e613d33565b606091505b509150915081613d8a576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b805115613de357808060200190516020811015613da657600080fd5b5051613de35760405162461bcd60e51b815260040180806020018281038252602a815260200180613ee1602a913960400191505060405180910390fd5b50505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590613e1d57508115155b949350505050565b604051806101800160405280600c905b613e4e815260200190600190039081613e355790505090565bfefe0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f73536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e63654f6e6553706c69743a20446973747269627574696f6e2061727261792073686f756c64206e6f74206578636565642072657365727665732061727261792073697a65a265627a7a72315820db0014d12533e61ce2b38dd56152e04b1f877c52472d06719a7a2dc73545dd2664736f6c63430005110032 \ No newline at end of file diff --git a/OneSplit.full.sol b/OneSplit.full.sol index 57c123a..eaff9df 100644 --- a/OneSplit.full.sol +++ b/OneSplit.full.sol @@ -5340,17 +5340,16 @@ pragma solidity ^0.5.0; - contract OneSplitSmartTokenBase { using SafeMath for uint256; - ISmartTokenRegistry smartTokenRegistry = ISmartTokenRegistry(0xf6E2D7F616B67E46D708e4410746E9AAb3a4C518); - ISmartTokenFormula smartTokenFormula = ISmartTokenFormula(0x524619EB9b4cdFFa7DA13029b33f24635478AFc0); - IERC20 bntToken = IERC20(0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C); - IERC20 usdbToken = IERC20(0x309627af60F0926daa6041B8279484312f2bf060); + ISmartTokenRegistry constant smartTokenRegistry = ISmartTokenRegistry(0xf6E2D7F616B67E46D708e4410746E9AAb3a4C518); + ISmartTokenFormula constant smartTokenFormula = ISmartTokenFormula(0x524619EB9b4cdFFa7DA13029b33f24635478AFc0); + IERC20 constant bntToken = IERC20(0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C); + IERC20 constant usdbToken = IERC20(0x309627af60F0926daa6041B8279484312f2bf060); - IERC20 public susd = IERC20(0x57Ab1ec28D129707052df4dF418D58a2D46d5f51); - IERC20 public acientSUSD = IERC20(0x57Ab1E02fEE23774580C119740129eAC7081e9D3); + IERC20 constant susd = IERC20(0x57Ab1ec28D129707052df4dF418D58a2D46d5f51); + IERC20 constant acientSUSD = IERC20(0x57Ab1E02fEE23774580C119740129eAC7081e9D3); struct TokenWithRatio { IERC20 token; @@ -5394,16 +5393,16 @@ contract OneSplitSmartTokenBase { ) ); - if (success) { - return abi.decode(data, (uint256)); - } + if (!success) { + (, uint32 ratio, , ,) = converter.connectors(address(token)); - (, uint32 ratio, , ,) = converter.connectors(address(token)); + return uint256(ratio); + } - return uint256(ratio); + return abi.decode(data, (uint256)); } - function _canonicalSUSD(IERC20 token) internal view returns(IERC20) { + function _canonicalSUSD(IERC20 token) internal pure returns(IERC20) { return token == acientSUSD ? susd : token; } } @@ -5596,34 +5595,6 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase } } - uint256 _minFundAmount = minFundAmount; - IERC20 _smartToken = smartToken; - - // Swap leftovers for SmartToken - for (uint i = 0; i < details.tokens.length; i++) { - if (_minFundAmount == fundAmounts[i]) { - continue; - } - - uint256 leftover = tokenAmounts[i].sub( - smartTokenFormula._calculateLiquidateReturn( - _smartToken.totalSupply().add(_minFundAmount), - _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter).add(tokenAmounts[i]), - uint32(details.totalRatio), - _minFundAmount - ) - ); - - uint256 tokenRet = _calculateBancorReturn( - _canonicalSUSD(details.tokens[i].token), - _smartToken, - leftover, - flags - ); - - minFundAmount = minFundAmount.add(tokenRet); - } - return (minFundAmount, distribution); } } @@ -5802,21 +5773,12 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { ISmartTokenConverter(details.converter).fund(minFundAmount); - // Swap leftovers for SmartToken for (uint i = 0; i < details.tokens.length; i++) { IERC20 reserveToken = _canonicalSUSD(details.tokens[i].token); - - uint256 leftover = _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)); - - uint256 ret = this._swapOnBancorSafe( - reserveToken, - smartToken, - leftover + reserveToken.universalTransfer( + msg.sender, + reserveToken.universalBalanceOf(address(this)) ); - - if (ret == 0) { - reserveToken.universalTransfer(msg.sender, leftover); - } } } } @@ -5875,7 +5837,17 @@ contract OneSplitUniswapV2PoolTokenBase { IUniswapV2Pool constant uniswapPool = IUniswapV2Pool(0x3f6CDd93e4A1c2Df9934Cb90D09040CcFc155F93); function isLiquidityPool(IERC20 token) internal view returns (bool) { - return IUniswapV2Pair(address(token)).factory() == 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; + (bool success, bytes memory data) = address(token).staticcall.gas(2000)( + abi.encode(IUniswapV2Pair(address(token)).factory.selector) + ); + if (!success) { + return false; + } + bytes memory emptyBytes; + if (keccak256(data) == keccak256(emptyBytes)) { + return false; + } + return abi.decode(data, (address)) == 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; } struct TokenInfo { @@ -6018,7 +5990,7 @@ contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswap continue; } - (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( + (uint256 ret, uint256[] memory dist) = this.getExpectedReturn( details.tokens[i].token, toToken, exchangeAmount, @@ -6064,7 +6036,7 @@ contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswap continue; } - (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( + (uint256 ret, uint256[] memory dist) = this.getExpectedReturn( fromToken, details.tokens[i].token, amounts[i], @@ -6281,18 +6253,18 @@ pragma solidity ^0.5.0; contract OneSplitViewWrap is OneSplitViewWrapBase, - OneSplitMultiPathView, + //OneSplitMultiPathView, OneSplitChaiView, - OneSplitBdaiView, - OneSplitAaveView, - OneSplitFulcrumView, - OneSplitCompoundView, - OneSplitIearnView, - OneSplitIdleView, + //OneSplitBdaiView, + //OneSplitAaveView, + //OneSplitFulcrumView, + //OneSplitCompoundView, + //OneSplitIearnView, + //OneSplitIdleView, OneSplitWethView, - OneSplitBalancerPoolTokenView, - OneSplitUniswapPoolTokenView, - OneSplitCurvePoolTokenView, + //OneSplitBalancerPoolTokenView, + //OneSplitUniswapPoolTokenView, + //OneSplitCurvePoolTokenView, OneSplitSmartTokenView, OneSplitUniswapV2PoolTokenView { @@ -6370,18 +6342,18 @@ contract OneSplitViewWrap is contract OneSplitWrap is OneSplitBaseWrap, - OneSplitMultiPath, + //OneSplitMultiPath, OneSplitChai, - OneSplitBdai, - OneSplitAave, - OneSplitFulcrum, - OneSplitCompound, - OneSplitIearn, - OneSplitIdle, + //OneSplitBdai, + //OneSplitAave, + //OneSplitFulcrum, + //OneSplitCompound, + //OneSplitIearn, + //OneSplitIdle, OneSplitWeth, - OneSplitBalancerPoolToken, - OneSplitUniswapPoolToken, - OneSplitCurvePoolToken, + //OneSplitBalancerPoolToken, + //OneSplitUniswapPoolToken, + //OneSplitCurvePoolToken, OneSplitSmartToken, OneSplitUniswapV2PoolToken { diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index c693145..33b336c 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -6,17 +6,16 @@ import "./interface/ISmartTokenConverter.sol"; import "./interface/ISmartTokenFormula.sol"; import "./OneSplitBase.sol"; - contract OneSplitSmartTokenBase { using SafeMath for uint256; - ISmartTokenRegistry smartTokenRegistry = ISmartTokenRegistry(0xf6E2D7F616B67E46D708e4410746E9AAb3a4C518); - ISmartTokenFormula smartTokenFormula = ISmartTokenFormula(0x524619EB9b4cdFFa7DA13029b33f24635478AFc0); - IERC20 bntToken = IERC20(0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C); - IERC20 usdbToken = IERC20(0x309627af60F0926daa6041B8279484312f2bf060); + ISmartTokenRegistry constant smartTokenRegistry = ISmartTokenRegistry(0xf6E2D7F616B67E46D708e4410746E9AAb3a4C518); + ISmartTokenFormula constant smartTokenFormula = ISmartTokenFormula(0x524619EB9b4cdFFa7DA13029b33f24635478AFc0); + IERC20 constant bntToken = IERC20(0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C); + IERC20 constant usdbToken = IERC20(0x309627af60F0926daa6041B8279484312f2bf060); - IERC20 public susd = IERC20(0x57Ab1ec28D129707052df4dF418D58a2D46d5f51); - IERC20 public acientSUSD = IERC20(0x57Ab1E02fEE23774580C119740129eAC7081e9D3); + IERC20 constant susd = IERC20(0x57Ab1ec28D129707052df4dF418D58a2D46d5f51); + IERC20 constant acientSUSD = IERC20(0x57Ab1E02fEE23774580C119740129eAC7081e9D3); struct TokenWithRatio { IERC20 token; @@ -60,16 +59,16 @@ contract OneSplitSmartTokenBase { ) ); - if (success) { - return abi.decode(data, (uint256)); - } + if (!success) { + (, uint32 ratio, , ,) = converter.connectors(address(token)); - (, uint32 ratio, , ,) = converter.connectors(address(token)); + return uint256(ratio); + } - return uint256(ratio); + return abi.decode(data, (uint256)); } - function _canonicalSUSD(IERC20 token) internal view returns(IERC20) { + function _canonicalSUSD(IERC20 token) internal pure returns(IERC20) { return token == acientSUSD ? susd : token; } } @@ -262,34 +261,6 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase } } - uint256 _minFundAmount = minFundAmount; - IERC20 _smartToken = smartToken; - - // Swap leftovers for SmartToken - for (uint i = 0; i < details.tokens.length; i++) { - if (_minFundAmount == fundAmounts[i]) { - continue; - } - - uint256 leftover = tokenAmounts[i].sub( - smartTokenFormula._calculateLiquidateReturn( - _smartToken.totalSupply().add(_minFundAmount), - _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter).add(tokenAmounts[i]), - uint32(details.totalRatio), - _minFundAmount - ) - ); - - uint256 tokenRet = _calculateBancorReturn( - _canonicalSUSD(details.tokens[i].token), - _smartToken, - leftover, - flags - ); - - minFundAmount = minFundAmount.add(tokenRet); - } - return (minFundAmount, distribution); } } @@ -468,21 +439,12 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { ISmartTokenConverter(details.converter).fund(minFundAmount); - // Swap leftovers for SmartToken for (uint i = 0; i < details.tokens.length; i++) { IERC20 reserveToken = _canonicalSUSD(details.tokens[i].token); - - uint256 leftover = _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)); - - uint256 ret = this._swapOnBancorSafe( - reserveToken, - smartToken, - leftover + reserveToken.universalTransfer( + msg.sender, + reserveToken.universalBalanceOf(address(this)) ); - - if (ret == 0) { - reserveToken.universalTransfer(msg.sender, leftover); - } } } } diff --git a/contracts/OneSplitUniswapV2PoolToken.sol b/contracts/OneSplitUniswapV2PoolToken.sol index f7dc2c9..4f37240 100644 --- a/contracts/OneSplitUniswapV2PoolToken.sol +++ b/contracts/OneSplitUniswapV2PoolToken.sol @@ -11,7 +11,17 @@ contract OneSplitUniswapV2PoolTokenBase { IUniswapV2Pool constant uniswapPool = IUniswapV2Pool(0x3f6CDd93e4A1c2Df9934Cb90D09040CcFc155F93); function isLiquidityPool(IERC20 token) internal view returns (bool) { - return IUniswapV2Pair(address(token)).factory() == 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; + (bool success, bytes memory data) = address(token).staticcall.gas(2000)( + abi.encode(IUniswapV2Pair(address(token)).factory.selector) + ); + if (!success) { + return false; + } + bytes memory emptyBytes; + if (keccak256(data) == keccak256(emptyBytes)) { + return false; + } + return abi.decode(data, (address)) == 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; } struct TokenInfo { @@ -154,7 +164,7 @@ contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswap continue; } - (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( + (uint256 ret, uint256[] memory dist) = this.getExpectedReturn( details.tokens[i].token, toToken, exchangeAmount, @@ -200,7 +210,7 @@ contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswap continue; } - (uint256 ret, uint256[] memory dist) = super.getExpectedReturn( + (uint256 ret, uint256[] memory dist) = this.getExpectedReturn( fromToken, details.tokens[i].token, amounts[i], From c55bb8e623b56609e3fa87487f8e0f94e4f65f33 Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 8 May 2020 19:26:05 +0300 Subject: [PATCH 55/60] fix bancor underscore in interrface --- contracts/OneSplitSmartToken.sol | 8 ++++---- contracts/interface/ISmartTokenFormula.sol | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 33b336c..0cea220 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -176,7 +176,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); for (uint i = 0; i < details.tokens.length; i++) { - uint256 srcAmount = smartTokenFormula._calculateLiquidateReturn( + uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( smartToken.totalSupply(), _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), uint32(details.totalRatio), @@ -249,7 +249,7 @@ contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase tokenAmounts[i] = exchangeAmount; } - fundAmounts[i] = smartTokenFormula._calculatePurchaseReturn( + fundAmounts[i] = smartTokenFormula.calculatePurchaseReturn( smartToken.totalSupply(), _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), uint32(details.totalRatio), @@ -415,14 +415,14 @@ contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { uint256 tokenBalanceAfter = _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)); - curFundAmount = smartTokenFormula._calculatePurchaseReturn( + curFundAmount = smartTokenFormula.calculatePurchaseReturn( smartToken.totalSupply(), _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), uint32(details.totalRatio), tokenBalanceAfter.sub(tokenBalanceBefore) ); } else { - curFundAmount = smartTokenFormula._calculatePurchaseReturn( + curFundAmount = smartTokenFormula.calculatePurchaseReturn( smartToken.totalSupply(), _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), uint32(details.totalRatio), diff --git a/contracts/interface/ISmartTokenFormula.sol b/contracts/interface/ISmartTokenFormula.sol index c4f2119..1dc791c 100644 --- a/contracts/interface/ISmartTokenFormula.sol +++ b/contracts/interface/ISmartTokenFormula.sol @@ -4,14 +4,14 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface ISmartTokenFormula { - function _calculateLiquidateReturn( + function calculateLiquidateReturn( uint256 supply, uint256 reserveBalance, uint32 totalRatio, uint256 amount ) external view returns (uint256); - function _calculatePurchaseReturn( + function calculatePurchaseReturn( uint256 supply, uint256 reserveBalance, uint32 totalRatio, From e6b0085062d62ab1320535faf84d7777f6379404 Mon Sep 17 00:00:00 2001 From: Kirill Date: Wed, 13 May 2020 00:46:42 +0300 Subject: [PATCH 56/60] change super -> this --- contracts/OneSplitUniswapV2PoolToken.sol | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/contracts/OneSplitUniswapV2PoolToken.sol b/contracts/OneSplitUniswapV2PoolToken.sol index 4f37240..07c3cf1 100644 --- a/contracts/OneSplitUniswapV2PoolToken.sol +++ b/contracts/OneSplitUniswapV2PoolToken.sol @@ -14,11 +14,7 @@ contract OneSplitUniswapV2PoolTokenBase { (bool success, bytes memory data) = address(token).staticcall.gas(2000)( abi.encode(IUniswapV2Pair(address(token)).factory.selector) ); - if (!success) { - return false; - } - bytes memory emptyBytes; - if (keccak256(data) == keccak256(emptyBytes)) { + if (!success || data.length == 0) { return false; } return abi.decode(data, (address)) == 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; @@ -343,10 +339,11 @@ contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTo dist[j] = (distribution[j] >> (i * 8)) & 0xFF; } - super._swap( + this.swap( details.tokens[i].token, toToken, amounts[i], + 0, dist, flags ); @@ -360,16 +357,13 @@ contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTo uint256[] memory distribution, uint256 flags ) private { - uint256[] memory dist = new uint256[](distribution.length); - - distribution = new uint256[](DEXES_COUNT); - PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); // will overwritten to liquidity amounts uint256[2] memory amounts; amounts[0] = amount.div(2); amounts[1] = amount.sub(amounts[0]); + uint256[] memory dist = new uint256[](distribution.length); for (uint i = 0; i < 2; i++) { _infiniteApproveIfNeeded(details.tokens[i].token, address(uniswapPool)); @@ -382,10 +376,11 @@ contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTo dist[j] = (distribution[j] >> (i * 8)) & 0xFF; } - super._swap( + this.swap( fromToken, details.tokens[i].token, amounts[i], + 0, dist, flags ); From 14fba802d10aae5748a8d641d1b2aec97c5178b4 Mon Sep 17 00:00:00 2001 From: Kirill Date: Wed, 13 May 2020 03:45:42 +0300 Subject: [PATCH 57/60] Add curve PAX ZAP --- contracts/OneSplitCurvePoolToken.sol | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/contracts/OneSplitCurvePoolToken.sol b/contracts/OneSplitCurvePoolToken.sol index 6a0abc6..889ba3f 100644 --- a/contracts/OneSplitCurvePoolToken.sol +++ b/contracts/OneSplitCurvePoolToken.sol @@ -13,12 +13,14 @@ contract OneSplitCurvePoolTokenBase { IERC20 constant curveCompoundToken = IERC20(0x845838DF265Dcd2c412A1Dc9e959c7d08537f8a2); IERC20 constant curveUsdtToken = IERC20(0x9fC689CCaDa600B6DF723D9E47D84d76664a1F23); IERC20 constant curveBinanceToken = IERC20(0x3B3Ac5386837Dc563660FB6a0937DFAa5924333B); + IERC20 constant curvePaxToken = IERC20(0xD905e2eaeBe188fc92179b6350807D8bd91Db0D8); ICurve constant curveSusd = ICurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); ICurve constant curveIearn = ICurve(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); ICurve constant curveCompound = ICurve(0xA2B47E3D5c44877cca798226B7B8118F9BFb7A56); ICurve constant curveUsdt = ICurve(0x52EA46506B9CC5Ef470C5bf89f17Dc28bB35D85C); ICurve constant curveBinance = ICurve(0x79a8C46DeA5aDa233ABaFFD40F3A0A2B1e5A4F27); + ICurve constant curvePax = ICurve(0x06364f10B501e868329afBc005b3492902d6C763); struct CurveTokenInfo { IERC20 token; @@ -45,7 +47,8 @@ contract OneSplitCurvePoolTokenBase { token == curveIearnToken || token == curveCompoundToken || token == curveUsdtToken || - token == curveBinanceToken + token == curveBinanceToken || + token == curvePaxToken ) { return true; } @@ -87,6 +90,12 @@ contract OneSplitCurvePoolTokenBase { return curveInfo; } + if (poolToken == curvePaxToken) { + curveInfo.curve = curvePax; + curveInfo.tokenCount = 4; + return curveInfo; + } + revert(); } From 7c1dd906b33ca92ef2e38537c8645badc6938a8d Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 18 May 2020 18:02:37 +0300 Subject: [PATCH 58/60] tmp curve renBTC --- contracts/OneSplit.sol | 40 ++++++++++++++-------------- contracts/OneSplitCurvePoolToken.sol | 20 +++++++++++++- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/contracts/OneSplit.sol b/contracts/OneSplit.sol index c9ee82f..04ef3ec 100644 --- a/contracts/OneSplit.sol +++ b/contracts/OneSplit.sol @@ -21,19 +21,19 @@ import "./OneSplitUniswapV2PoolToken.sol"; contract OneSplitViewWrap is OneSplitViewWrapBase, OneSplitMultiPathView, - OneSplitChaiView, - OneSplitBdaiView, - OneSplitAaveView, - OneSplitFulcrumView, + //OneSplitChaiView, + //OneSplitBdaiView, + //OneSplitAaveView, + //OneSplitFulcrumView, OneSplitCompoundView, OneSplitIearnView, - OneSplitIdleView, + //OneSplitIdleView, OneSplitWethView, - OneSplitBalancerPoolTokenView, - OneSplitUniswapPoolTokenView, - OneSplitCurvePoolTokenView, - OneSplitSmartTokenView, - OneSplitUniswapV2PoolTokenView + //OneSplitBalancerPoolTokenView, + //OneSplitUniswapPoolTokenView, + OneSplitCurvePoolTokenView + //OneSplitSmartTokenView, + //OneSplitUniswapV2PoolTokenView { IOneSplitView public oneSplitView; @@ -96,19 +96,19 @@ contract OneSplitViewWrap is contract OneSplitWrap is OneSplitBaseWrap, OneSplitMultiPath, - OneSplitChai, - OneSplitBdai, - OneSplitAave, - OneSplitFulcrum, + //OneSplitChai, + //OneSplitBdai, + //OneSplitAave, + //OneSplitFulcrum, OneSplitCompound, OneSplitIearn, - OneSplitIdle, + //OneSplitIdle, OneSplitWeth, - OneSplitBalancerPoolToken, - OneSplitUniswapPoolToken, - OneSplitCurvePoolToken, - OneSplitSmartToken, - OneSplitUniswapV2PoolToken + //OneSplitBalancerPoolToken, + //OneSplitUniswapPoolToken, + OneSplitCurvePoolToken + //OneSplitSmartToken, + //OneSplitUniswapV2PoolToken { IOneSplitView public oneSplitView; IOneSplit public oneSplit; diff --git a/contracts/OneSplitCurvePoolToken.sol b/contracts/OneSplitCurvePoolToken.sol index 889ba3f..55f746c 100644 --- a/contracts/OneSplitCurvePoolToken.sol +++ b/contracts/OneSplitCurvePoolToken.sol @@ -14,6 +14,8 @@ contract OneSplitCurvePoolTokenBase { IERC20 constant curveUsdtToken = IERC20(0x9fC689CCaDa600B6DF723D9E47D84d76664a1F23); IERC20 constant curveBinanceToken = IERC20(0x3B3Ac5386837Dc563660FB6a0937DFAa5924333B); IERC20 constant curvePaxToken = IERC20(0xD905e2eaeBe188fc92179b6350807D8bd91Db0D8); + IERC20 constant curveRenBtcToken = IERC20(0x7771F704490F9C0C3B06aFe8960dBB6c58CBC812); + IERC20 constant curveTBtcToken = IERC20(0x1f2a662FB513441f06b8dB91ebD9a1466462b275); ICurve constant curveSusd = ICurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); ICurve constant curveIearn = ICurve(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); @@ -21,6 +23,8 @@ contract OneSplitCurvePoolTokenBase { ICurve constant curveUsdt = ICurve(0x52EA46506B9CC5Ef470C5bf89f17Dc28bB35D85C); ICurve constant curveBinance = ICurve(0x79a8C46DeA5aDa233ABaFFD40F3A0A2B1e5A4F27); ICurve constant curvePax = ICurve(0x06364f10B501e868329afBc005b3492902d6C763); + ICurve constant curveRenBtc = ICurve(0x8474c1236F0Bc23830A23a41aBB81B2764bA9f4F); + ICurve constant curveTBtc = ICurve(0x9726e9314eF1b96E45f40056bEd61A088897313E); struct CurveTokenInfo { IERC20 token; @@ -48,7 +52,9 @@ contract OneSplitCurvePoolTokenBase { token == curveCompoundToken || token == curveUsdtToken || token == curveBinanceToken || - token == curvePaxToken + token == curvePaxToken || + token == curveRenBtcToken || + token == curveTBtcToken ) { return true; } @@ -96,6 +102,18 @@ contract OneSplitCurvePoolTokenBase { return curveInfo; } + if (poolToken == curveRenBtcToken) { + curveInfo.curve = curveRenBtc; + curveInfo.tokenCount = 2; + return curveInfo; + } + + if (poolToken == curveTBtcToken) { + curveInfo.curve = curveTBtc; + curveInfo.tokenCount = 3; + return curveInfo; + } + revert(); } From cb716ecd08283acd5d462592de29aa91a27300bf Mon Sep 17 00:00:00 2001 From: Kirill Date: Wed, 20 May 2020 01:33:08 +0300 Subject: [PATCH 59/60] tmp fix --- OneSplit.full.abi | 2 +- OneSplit.full.bin | 2 +- OneSplit.full.sol | 2751 +++++++++++++++++++++- contracts/IOneSplit.sol | 1 + contracts/OneSplit.sol | 30 +- contracts/OneSplitBalancerPoolToken.sol | 1 + contracts/OneSplitCurvePoolToken.sol | 6 +- contracts/OneSplitSmartToken.sol | 1 + contracts/OneSplitUniswapPoolToken.sol | 1 + contracts/OneSplitUniswapV2PoolToken.sol | 206 +- contracts/interface/IBPool.sol | 3 + contracts/interface/IUniswapV2Pool.sol | 21 - contracts/interface/IUniswapV2Router.sol | 36 + test/OneSplit.js | 1 - 14 files changed, 2912 insertions(+), 150 deletions(-) delete mode 100644 contracts/interface/IUniswapV2Pool.sol create mode 100644 contracts/interface/IUniswapV2Router.sol diff --git a/OneSplit.full.abi b/OneSplit.full.abi index 7309c9e..4d8e3b8 100644 --- a/OneSplit.full.abi +++ b/OneSplit.full.abi @@ -1 +1 @@ -[{"inputs":[{"internalType":"contract IOneSplitView","name":"_oneSplitView","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[],"name":"DEXES_COUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ETH_ADDRESS","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_ALL_SPLIT_SOURCES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_ALL_WRAP_SOURCES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BANCOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BDAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_BINANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_PAX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_SYNTHETIX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_USDT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_Y","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_FULCRUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IDLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IEARN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_KYBER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_MOONISWAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_OASIS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_SMART_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_V2","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_V2_ALL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_V2_DAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_V2_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_V2_USDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_WETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_BANCOR_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_OASIS_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_UNISWAP_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_DAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_USDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"aave","outputs":[{"internalType":"contract IAaveLendingPool","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorContractRegistry","outputs":[{"internalType":"contract IBancorContractRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorConverterRegistry","outputs":[{"internalType":"contract IBancorConverterRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorEtherToken","outputs":[{"internalType":"contract IBancorEtherToken","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bnt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"busd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cETH","outputs":[{"internalType":"contract ICompoundEther","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"chai","outputs":[{"internalType":"contract IChai","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"compound","outputs":[{"internalType":"contract ICompound","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveBinance","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveCompound","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curvePax","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveSynthetix","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveUsdt","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveY","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"}],"name":"getExpectedReturn","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"kyberNetworkProxy","outputs":[{"internalType":"contract IKyberNetworkProxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"mooniswapRegistry","outputs":[{"internalType":"contract IMooniswapRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oasisExchange","outputs":[{"internalType":"contract IOasisExchange","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oneSplitView","outputs":[{"internalType":"contract IOneSplitView","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pax","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"susd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"swap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"tusd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"uniswapFactory","outputs":[{"internalType":"contract IUniswapFactory","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"uniswapV2","outputs":[{"internalType":"contract IUniswapV2Factory","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdc","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"weth","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file +[{"inputs":[{"internalType":"contract IOneSplitView","name":"_oneSplitView","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":true,"inputs":[],"name":"DEXES_COUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ETH_ADDRESS","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_ALL_SPLIT_SOURCES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_ALL_WRAP_SOURCES","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BALANCER_POOL_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BANCOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_BDAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_BINANCE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_PAX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_SYNTHETIX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_USDT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_Y","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_CURVE_ZAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_FULCRUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IDLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_IEARN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_KYBER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_MOONISWAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_OASIS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_SMART_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_POOL_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_V2","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_V2_ALL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_V2_DAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_V2_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_UNISWAP_V2_USDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_DISABLE_WETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_BANCOR_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_OASIS_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_KYBER_UNISWAP_RESERVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_DAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_MULTI_PATH_USDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_AAVE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_CHAI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FLAG_ENABLE_UNISWAP_COMPOUND","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"aave","outputs":[{"internalType":"contract IAaveLendingPool","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorContractRegistry","outputs":[{"internalType":"contract IBancorContractRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorConverterRegistry","outputs":[{"internalType":"contract IBancorConverterRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bancorEtherToken","outputs":[{"internalType":"contract IBancorEtherToken","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"bnt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"busd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cETH","outputs":[{"internalType":"contract ICompoundEther","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"chai","outputs":[{"internalType":"contract IChai","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"compound","outputs":[{"internalType":"contract ICompound","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveBinance","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveCompound","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curvePax","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveSynthetix","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveUsdt","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"curveY","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dai","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"parts","type":"uint256"},{"internalType":"uint256","name":"flags","type":"uint256"}],"name":"getExpectedReturn","outputs":[{"internalType":"uint256","name":"returnAmount","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"kyberNetworkProxy","outputs":[{"internalType":"contract IKyberNetworkProxy","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"mooniswapRegistry","outputs":[{"internalType":"contract IMooniswapRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oasisExchange","outputs":[{"internalType":"contract IOasisExchange","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oneSplitView","outputs":[{"internalType":"contract IOneSplitView","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pax","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"susd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IERC20","name":"fromToken","type":"address"},{"internalType":"contract IERC20","name":"toToken","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256[]","name":"distribution","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"swap","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"tusd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"uniswapFactory","outputs":[{"internalType":"contract IUniswapFactory","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"uniswapV2","outputs":[{"internalType":"contract IUniswapV2Factory","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdc","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"usdt","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"weth","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/OneSplit.full.bin b/OneSplit.full.bin index 4b02744..bf776e4 100644 --- a/OneSplit.full.bin +++ b/OneSplit.full.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b5060405162004a2f38038062004a2f8339818101604052602081101561003557600080fd5b5051600080546001600160a01b039092166001600160a01b03199092169190911790556149c780620000686000396000f3fe6080604052600436106103ef5760003560e01c80637e09b9c211610208578063c989b66711610118578063dc1536b2116100ab578063f4b9fa751161007a578063f4b9fa7514610aa1578063f56e281f14610ab6578063f69e204614610acb578063fa3f110b14610ae0578063fbe4ed9514610af5576103ef565b8063dc1536b214610998578063e2a7515e146109ad578063e355812314610a77578063e44987b414610a8c576103ef565b8063cede5f6a116100e7578063cede5f6a14610944578063d393c3e914610959578063d70a2d1f1461096e578063d77366a414610983576103ef565b8063c989b667146108f0578063c9b42c6714610905578063cc26e9fc1461091a578063ce74b7ac1461092f576103ef565b8063b0a7ef291161019b578063bf2c5a071161016a578063bf2c5a0714610887578063c762a46c1461089c578063c77b9de6146108b1578063c7f112e4146108c6578063c9257775146108db576103ef565b8063b0a7ef2914610833578063b184a3ae14610848578063b3bc78441461085d578063b69d045614610872576103ef565b8063a1b4d011116101d7578063a1b4d011146107df578063a2878cb1146107f4578063a4792ab314610809578063a734f06e1461081e576103ef565b80637e09b9c21461078b578063819faf7b146107a0578063851954fa146107b55780638bdb2afa146107ca576103ef565b806340ab7b8c116103035780635aa8fb481161029657806368e2a0141161026557806368e2a014146107225780636cbc4a6e1461073757806375a8b0121461074c57806375b5be2d146107615780637a88bdbd14610776576103ef565b80635aa8fb48146106ce5780635ae51b82146106e35780635c0cb479146106f857806364ec4e5c1461070d576103ef565b80634a7101d5116102d25780634a7101d51461067a5780635187c0911461068f57806351f1985c146106a457806352a701b4146106b9576103ef565b806340ab7b8c14610626578063423d03f91461063b57806344211d62146106505780634752c68014610665576103ef565b806322320c981161038657806334b4dabb1161035557806334b4dabb146105bd578063372a26cb146105d25780633ca5b234146105e75780633e413bee146105fc5780633fc8cef314610611576103ef565b806322320c98146105695780632d3b52071461057e5780632e707bd2146105935780632f48ab7d146105a8576103ef565b806313989140116103c257806313989140146105155780631d209b651461052a5780632113240d1461053f57806321a360f514610554576103ef565b806305d8aa0a146103fe578063085e2c5b1461042557806312dea160146104cf5780631388b42014610500575b333214156103fc57600080fd5b005b34801561040a57600080fd5b50610413610b0a565b60408051918252519081900360200190f35b34801561043157600080fd5b50610474600480360360a081101561044857600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060800135610b11565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b838110156104ba5781810151838201526020016104a2565b50505050905001935050505060405180910390f35b3480156104db57600080fd5b506104e4610c5d565b604080516001600160a01b039092168252519081900360200190f35b34801561050c57600080fd5b506104e4610c75565b34801561052157600080fd5b50610413610c8d565b34801561053657600080fd5b50610413610c93565b34801561054b57600080fd5b50610413610c9b565b34801561056057600080fd5b50610413610ca1565b34801561057557600080fd5b506104e4610caa565b34801561058a57600080fd5b50610413610cc2565b34801561059f57600080fd5b50610413610ccb565b3480156105b457600080fd5b506104e4610cd0565b3480156105c957600080fd5b50610413610ce2565b3480156105de57600080fd5b506104e4610ce7565b3480156105f357600080fd5b506104e4610cff565b34801561060857600080fd5b506104e4610d17565b34801561061d57600080fd5b506104e4610d29565b34801561063257600080fd5b506104e4610d41565b34801561064757600080fd5b506104e4610d59565b34801561065c57600080fd5b50610413610d71565b34801561067157600080fd5b50610413610d76565b34801561068657600080fd5b50610413610d7e565b34801561069b57600080fd5b506104e4610d83565b3480156106b057600080fd5b506104e4610d9b565b3480156106c557600080fd5b506104e4610db3565b3480156106da57600080fd5b50610413610dcb565b3480156106ef57600080fd5b50610413610dd1565b34801561070457600080fd5b50610413610dd7565b34801561071957600080fd5b50610413610ddc565b34801561072e57600080fd5b50610413610de3565b34801561074357600080fd5b50610413610dea565b34801561075857600080fd5b50610413610df1565b34801561076d57600080fd5b506104e4610df7565b34801561078257600080fd5b50610413610e0a565b34801561079757600080fd5b50610413610e0f565b3480156107ac57600080fd5b506104e4610e16565b3480156107c157600080fd5b506104e4610e2e565b3480156107d657600080fd5b506104e4610e46565b3480156107eb57600080fd5b506104e4610e5e565b34801561080057600080fd5b50610413610e76565b34801561081557600080fd5b506104e4610e7e565b34801561082a57600080fd5b506104e4610e96565b34801561083f57600080fd5b50610413610eae565b34801561085457600080fd5b506104e4610eb4565b34801561086957600080fd5b50610413610ecc565b34801561087e57600080fd5b506104e4610ed5565b34801561089357600080fd5b50610413610eed565b3480156108a857600080fd5b50610413610ef5565b3480156108bd57600080fd5b50610413610efa565b3480156108d257600080fd5b50610413610f00565b3480156108e757600080fd5b506104e4610f08565b3480156108fc57600080fd5b50610413610f20565b34801561091157600080fd5b50610413610f27565b34801561092657600080fd5b50610413610f2e565b34801561093b57600080fd5b50610413610f33565b34801561095057600080fd5b506104e4610f3b565b34801561096557600080fd5b50610413610f53565b34801561097a57600080fd5b506104e4610f5a565b34801561098f57600080fd5b506104e4610f72565b3480156109a457600080fd5b50610413610f8a565b6103fc600480360360c08110156109c357600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135640100000000811115610a0357600080fd5b820183602082011115610a1557600080fd5b80359060200191846020830284011164010000000083111715610a3757600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610f90915050565b348015610a8357600080fd5b506104136111e2565b348015610a9857600080fd5b506104136111ea565b348015610aad57600080fd5b506104e46111f2565b348015610ac257600080fd5b50610413611204565b348015610ad757600080fd5b506104e4611209565b348015610aec57600080fd5b50610413611221565b348015610b0157600080fd5b506104e4611229565b6220000081565b600080546040805163085e2c5b60e01b81526001600160a01b03898116600483015288811660248301526044820188905260648201879052608482018690529151606093929092169163085e2c5b9160a4808201928792909190829003018186803b158015610b7f57600080fd5b505afa158015610b93573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610bbc57600080fd5b815160208301805160405192949293830192919084640100000000821115610be357600080fd5b908301906020820185811115610bf857600080fd5b8251866020820283011164010000000082111715610c1557600080fd5b82525081516020918201928201910280838360005b83811015610c42578181015183820152602001610c2a565b50505050905001604052505050915091509550959350505050565b7352ae12abe5d8bd778bd5397f99ca900624cfadd481565b73794e6e91555438afc3ccf1c5076a74f42133d08d81565b61200081565b630400000081565b61800081565b64020000000081565b73a5407eae9ba41422680e2e00537571bcc53efbfd81565b64010000000081565b608081565b6000805160206148d183398151915281565b604081565b7379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2781565b734fabb145d64652a948d72533023f6e7a623c7c5381565b6000805160206148b183398151915281565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c81565b7345f783cce6b7ff23b2ab2d70e416cdb7d6055f5181565b601081565b631e00000081565b602081565b735c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f81565b73a2b47e3d5c44877cca798226b7b8118f9bfb7a5681565b738e870d67f660d95d5be530380d0ec0bd388289e181565b61400081565b61080081565b600881565b6202000081565b6210000081565b6208000081565b61040081565b6e085d4780b73119b644ae5ecd22b37681565b600281565b6240000081565b73398ec7346dcd622edc5ae82352f02be94c62d11981565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce31581565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9581565b734ddc2d193948926d02f9b1fe9e1daa0718270ed581565b634000000081565b737079e8517594e5b21d2b9a0d17cb33f5fe2bca7081565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b7306364f10b501e868329afbc005b3492902d6c76381565b64040000000081565b7306af07097c9eeb7fd685c692751d5c66db49c21581565b630800000081565b600181565b61020081565b638000000081565b7357ab1ec28d129707052df4df418d58a2d46d5f5181565b6280000081565b6204000081565b601281565b630200000081565b7352ea46506b9cc5ef470c5bf89f17dc28bb35d85c81565b6201000081565b73f6e2d7f616b67e46d708e4410746e9aab3a4c51881565b73818e6fecd516ecc3849daf6845e3ec868087b75581565b61010081565b846001600160a01b0316866001600160a01b03161415610faf576111da565b610fb7614815565b60405180610240016040528061123881526020016114b9815260200161164481526020016119698152602001611c428152602001611dcd8152602001611f9281526020016121ab81526020016123ce81526020016125f1815260200161278f815260200161293b8152602001612aa78152602001612bef8152602001612bfc8152602001612c1e8152602001612c3a8152602001612c5681525090506012835111156110945760405162461bcd60e51b81526004018080602001828103825260428152602001806149516042913960600191505060405180910390fd5b600080805b85518110156110f25760008682815181106110b057fe5b602002602001015111156110ea576110e48682815181106110cd57fe5b602002602001015184612e7990919063ffffffff16565b92508091505b600101611099565b50600082116111325760405162461bcd60e51b815260040180806020018281038252602f815260200180614861602f913960400191505060405180910390fd5b8660005b86518110156111d45786818151811061114b57fe5b602002602001015160001415611160576111cc565b60006111988561118c8a858151811061117557fe5b60200260200101518d612edc90919063ffffffff16565b9063ffffffff612f3516565b9050838214156111a55750815b80830392506111c98c8c838986601281106111bc57fe5b602002015163ffffffff16565b50505b600101611136565b50505050505b505050505050565b631000000081565b632000000081565b60008051602061484183398151915281565b600481565b733d9819210a31b4961b30ef54be2aed79b9c9cd3b81565b630100000081565b6000546001600160a01b031681565b60008161124d6001600160a01b038616612f77565b61137d57604080516303795fb160e11b81526001600160a01b0387166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156112af57600080fd5b505afa1580156112c3573d6000803e3d6000fd5b505050506040513d60208110156112d957600080fd5b505190506001600160a01b0381161561137b576112f68682612fb3565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561134c57600080fd5b505af1158015611360573d6000803e3d6000fd5b505050506040513d602081101561137657600080fd5b505191505b505b61138f846001600160a01b0316612f77565b6114af57604080516303795fb160e11b81526001600160a01b0386166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156113f157600080fd5b505afa158015611405573d6000803e3d6000fd5b505050506040513d602081101561141b57600080fd5b505190506001600160a01b038116156114ad57806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b15801561147d57600080fd5b505af1158015611491573d6000803e3d6000fd5b50505050506040513d60208110156114a857600080fd5b505191505b505b90505b9392505050565b60006114d98473818e6fecd516ecc3849daf6845e3ec868087b755612fb3565b73818e6fecd516ecc3849daf6845e3ec868087b7556329589f616115056001600160a01b038716612f77565b611510576000611512565b835b611524876001600160a01b0316612f77565b61152e5786611544565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b85611557886001600160a01b0316612f77565b6115615787611577565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b15801561160f57600080fd5b505af1158015611623573d6000803e3d6000fd5b50505050506040513d602081101561163a57600080fd5b5051949350505050565b6000611658846001600160a01b0316612f77565b156116c65773c0829421c1d260bd3cb3e0f06cfe2d52db2ce3156001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156116ac57600080fd5b505af11580156116c0573d6000803e3d6000fd5b50505050505b60007352ae12abe5d8bd778bd5397f99ca900624cfadd46001600160a01b031663bb34534c6040518163ffffffff1660e01b815260040180806c42616e636f724e6574776f726b60981b815250602001905060206040518083038186803b15801561173057600080fd5b505afa158015611744573d6000803e3d6000fd5b505050506040513d602081101561175a57600080fd5b50519050606061176a868661306c565b90506117a7611781876001600160a01b0316612f77565b61178b57866117a1565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b83612fb3565b6000826001600160a01b031663c7ba24bc838760016040518463ffffffff1660e01b81526004018080602001848152602001838152602001828103825285818151815260200191508051906020019060200280838360005b838110156118175781810151838201526020016117ff565b50505050905001945050505050602060405180830381600087803b15801561183e57600080fd5b505af1158015611852573d6000803e3d6000fd5b505050506040513d602081101561186857600080fd5b5051905061187e6001600160a01b038716612f77565b1561195f57604080516370a0823160e01b8152306004820152905173c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591632e1a7d4d9183916370a08231916024808301926020929190829003018186803b1580156118dc57600080fd5b505afa1580156118f0573d6000803e3d6000fd5b505050506040513d602081101561190657600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561194657600080fd5b505af115801561195a573d6000803e3d6000fd5b505050505b9695505050505050565b600061197d846001600160a01b0316612f77565b156119eb5773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156119d157600080fd5b505af11580156119e5573d6000803e3d6000fd5b50505050505b611a3a611a00856001600160a01b0316612f77565b611a0a5784611a20565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b73794e6e91555438afc3ccf1c5076a74f42133d08d612fb3565b600073794e6e91555438afc3ccf1c5076a74f42133d08d630621b4f6611a686001600160a01b038816612f77565b611a725786611a88565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b85611a9b886001600160a01b0316612f77565b611aa55787611abb565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b158015611b1957600080fd5b505af1158015611b2d573d6000803e3d6000fd5b505050506040513d6020811015611b4357600080fd5b50519050611b596001600160a01b038516612f77565b156114af57604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b158015611bb757600080fd5b505afa158015611bcb573d6000803e3d6000fd5b505050506040513d6020811015611be157600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015611c2157600080fd5b505af1158015611c35573d6000803e3d6000fd5b5050505090509392505050565b6000806001600160a01b0385166000805160206148b183398151915214611c6a576000611c6d565b60025b6001600160a01b03861660008051602061484183398151915214611c92576000611c95565b60015b0160ff16905060006000805160206148b18339815191526001600160a01b03861614611cc2576000611cc5565b60025b6001600160a01b03861660008051602061484183398151915214611cea576000611ced565b60015b0160ff16905081600f0b60001480611d08575080600f0b6000145b15611d18576000925050506114b2565b611d368673a2b47e3d5c44877cca798226b7b8118f9bfb7a56612fb3565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a2b47e3d5c44877cca798226b7b8118f9bfb7a569263a6417ed6926084808201939182900301818387803b158015611dac57600080fd5b505af1158015611dc0573d6000803e3d6000fd5b5050505050509392505050565b6000806001600160a01b0385166000805160206148d183398151915214611df5576000611df8565b60035b6001600160a01b0386166000805160206148b183398151915214611e1d576000611e20565b60025b6001600160a01b03871660008051602061484183398151915214611e45576000611e48565b60015b010160ff16905060006000805160206148d18339815191526001600160a01b0316856001600160a01b031614611e7f576000611e82565b60035b6001600160a01b0386166000805160206148b183398151915214611ea7576000611eaa565b60025b6001600160a01b03871660008051602061484183398151915214611ecf576000611ed2565b60015b010160ff16905081600f0b60001480611eee575080600f0b6000145b15611efe576000925050506114b2565b611f1c867352ea46506b9cc5ef470c5bf89f17dc28bb35d85c612fb3565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517352ea46506b9cc5ef470c5bf89f17dc28bb35d85c9263a6417ed6926084808201939182900301818387803b158015611dac57600080fd5b6000806001600160a01b0385166e085d4780b73119b644ae5ecd22b37614611fbb576000611fbe565b60045b6001600160a01b0386166000805160206148d183398151915214611fe3576000611fe6565b60035b6001600160a01b0387166000805160206148b18339815191521461200b57600061200e565b60025b6001600160a01b03881660008051602061484183398151915214612033576000612036565b60015b01010160ff16905060006e085d4780b73119b644ae5ecd22b3766001600160a01b0316856001600160a01b03161461206f576000612072565b60045b6001600160a01b0386166000805160206148d18339815191521461209757600061209a565b60035b6001600160a01b0387166000805160206148b1833981519152146120bf5760006120c2565b60025b6001600160a01b038816600080516020614841833981519152146120e75760006120ea565b60015b01010160ff16905081600f0b60001480612107575080600f0b6000145b15612117576000925050506114b2565b612135867345f783cce6b7ff23b2ab2d70e416cdb7d6055f51612fb3565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517345f783cce6b7ff23b2ab2d70e416cdb7d6055f519263a6417ed6926084808201939182900301818387803b158015611dac57600080fd5b6000806001600160a01b038516734fabb145d64652a948d72533023f6e7a623c7c53146121d95760006121dc565b60045b6001600160a01b0386166000805160206148d183398151915214612201576000612204565b60035b6001600160a01b0387166000805160206148b18339815191521461222957600061222c565b60025b6001600160a01b03881660008051602061484183398151915214612251576000612254565b60015b01010160ff1690506000734fabb145d64652a948d72533023f6e7a623c7c536001600160a01b0316856001600160a01b031614612292576000612295565b60045b6001600160a01b0386166000805160206148d1833981519152146122ba5760006122bd565b60035b6001600160a01b0387166000805160206148b1833981519152146122e25760006122e5565b60025b6001600160a01b0388166000805160206148418339815191521461230a57600061230d565b60015b01010160ff16905081600f0b6000148061232a575080600f0b6000145b1561233a576000925050506114b2565b612358867379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27612fb3565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517379a8c46dea5ada233abaffd40f3a0a2b1e5a4f279263a6417ed6926084808201939182900301818387803b158015611dac57600080fd5b6000806001600160a01b0385167357ab1ec28d129707052df4df418d58a2d46d5f51146123fc5760006123ff565b60045b6001600160a01b0386166000805160206148d183398151915214612424576000612427565b60035b6001600160a01b0387166000805160206148b18339815191521461244c57600061244f565b60025b6001600160a01b03881660008051602061484183398151915214612474576000612477565b60015b01010160ff16905060007357ab1ec28d129707052df4df418d58a2d46d5f516001600160a01b0316856001600160a01b0316146124b55760006124b8565b60045b6001600160a01b0386166000805160206148d1833981519152146124dd5760006124e0565b60035b6001600160a01b0387166000805160206148b183398151915214612505576000612508565b60025b6001600160a01b0388166000805160206148418339815191521461252d576000612530565b60015b01010160ff16905081600f0b6000148061254d575080600f0b6000145b1561255d576000925050506114b2565b61257b8673a5407eae9ba41422680e2e00537571bcc53efbfd612fb3565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a5407eae9ba41422680e2e00537571bcc53efbfd9263a6417ed6926084808201939182900301818387803b158015611dac57600080fd5b6000612605846001600160a01b0316612f77565b6126be57600061261485613776565b90506126208582612fb3565b806001600160a01b031663a0712d68846040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561266657600080fd5b505af115801561267a573d6000803e3d6000fd5b505050506040513d602081101561269057600080fd5b506126b6905081856126b16001600160a01b0383163063ffffffff61396016565b611238565b9150506114b2565b6126d0836001600160a01b0316612f77565b6127855760006126df84613776565b905060006126ee868386611238565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561273657600080fd5b505af115801561274a573d6000803e3d6000fd5b505050506040513d602081101561276057600080fd5b5061277c90506001600160a01b0386163063ffffffff61396016565b925050506114b2565b5060009392505050565b60006001600160a01b0384166000805160206148418339815191521415612872576127ce847306af07097c9eeb7fd685c692751d5c66db49c215612fb3565b60408051633b4da69f60e01b81523060048201526024810184905290517306af07097c9eeb7fd685c692751d5c66db49c21591633b4da69f91604480830192600092919082900301818387803b15801561282757600080fd5b505af115801561283b573d6000803e3d6000fd5b5061286b92507306af07097c9eeb7fd685c692751d5c66db49c21591508590506126b1823063ffffffff61396016565b90506114b2565b6001600160a01b03831660008051602061484183398151915214156127855760006128b2857306af07097c9eeb7fd685c692751d5c66db49c21585611238565b6040805163ef693bed60e01b81523060048201526024810183905290519192507306af07097c9eeb7fd685c692751d5c66db49c2159163ef693bed9160448082019260009290919082900301818387803b15801561290f57600080fd5b505af1158015612923573d6000803e3d6000fd5b506126b6925050506001600160a01b03851630613960565b600061294f846001600160a01b0316612f77565b612a0d57600061295e85613a0a565b905061296a8582612fb3565b60408051636968703360e11b81526001600160a01b03871660048201526024810185905261044d6044820152905173398ec7346dcd622edc5ae82352f02be94c62d1199163d2d0e06691606480830192600092919082900301818387803b1580156129d457600080fd5b505af11580156129e8573d6000803e3d6000fd5b505050506126b681856126b130856001600160a01b031661396090919063ffffffff16565b612a1f836001600160a01b0316612f77565b612785576000612a2e84613a0a565b90506000612a3d868386611238565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015612a8557600080fd5b505af1158015612a99573d6000803e3d6000fd5b5050505080925050506114b2565b600080737079e8517594e5b21d2b9a0d17cb33f5fe2bca706001600160a01b031663d4b839926040518163ffffffff1660e01b815260040160206040518083038186803b158015612af757600080fd5b505afa158015612b0b573d6000803e3d6000fd5b505050506040513d6020811015612b2157600080fd5b50519050612b2f8582612fb3565b806001600160a01b031663fe029156612b50876001600160a01b0316612f77565b612b5b576000612b5d565b845b604080516001600160e01b031960e085901b1681526001600160a01b03808b1660048301528916602482015260448101889052600060648201529051608480830192602092919082900301818588803b158015612bb957600080fd5b505af1158015612bcd573d6000803e3d6000fd5b50505050506040513d6020811015612be457600080fd5b505195945050505050565b60006114af848484613e27565b60006114af8473c02aaa39b223fe8d0a0e5c4f27ead9083c756cc285856141f0565b60006114af8460008051602061484183398151915285856141f0565b60006114af846000805160206148b183398151915285856141f0565b6000806001600160a01b038516738e870d67f660d95d5be530380d0ec0bd388289e114612c84576000612c87565b60045b6001600160a01b0386166000805160206148d183398151915214612cac576000612caf565b60035b6001600160a01b0387166000805160206148b183398151915214612cd4576000612cd7565b60025b6001600160a01b03881660008051602061484183398151915214612cfc576000612cff565b60015b01010160ff1690506000738e870d67f660d95d5be530380d0ec0bd388289e16001600160a01b0316856001600160a01b031614612d3d576000612d40565b60045b6001600160a01b0386166000805160206148d183398151915214612d65576000612d68565b60035b6001600160a01b0387166000805160206148b183398151915214612d8d576000612d90565b60025b6001600160a01b03881660008051602061484183398151915214612db5576000612db8565b60015b01010160ff16905081600f0b60001480612dd5575080600f0b6000145b15612de5576000925050506114b2565b612e03867306364f10b501e868329afbc005b3492902d6c763612fb3565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517306364f10b501e868329afbc005b3492902d6c7639263a6417ed6926084808201939182900301818387803b158015611dac57600080fd5b600082820183811015612ed3576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b600082612eeb57506000612ed6565b82820282848281612ef857fe5b0414612ed35760405162461bcd60e51b81526004018080602001828103825260218152602001806148906021913960400191505060405180910390fd5b6000612ed383836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614210565b60006001600160a01b0382161580612fab57506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b90505b919050565b612fc5826001600160a01b0316612f77565b6130685760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b15801561301a57600080fd5b505afa15801561302e573d6000803e3d6000fd5b505050506040513d602081101561304457600080fd5b5051901c613068576130686001600160a01b0383168260001963ffffffff6142b216565b5050565b6060816001600160a01b0316836001600160a01b0316141561309d5750604080516000815260208101909152612ed6565b6130af836001600160a01b0316612f77565b156130cc5773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31592505b6130de826001600160a01b0316612f77565b156130fb5773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591505b6001600160a01b038316731f573d6fb3f13d689ff844b4ce37794d79a7ff1c148061314257506001600160a01b038216731f573d6fb3f13d689ff844b4ce37794d79a7ff1c145b1561316d5760408051600380825260808201909252906020820160608038833901905050905061318f565b60408051600580825260c08201909252906020820160a0803883390190505090505b6000806001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14613358576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6131ec6001600160a01b038b16612f77565b6131f6578961320c565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b6020831061328a5780518252601f19909201916020918201910161326b565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d80600081146132eb576040519150601f19603f3d011682016040523d82523d6000602084013e6132f0565b606091505b5091509150816133185760408051600080825260208201909252905b50945050505050612ed6565b80806020019051602081101561332d57600080fd5b505193506001600160a01b03841661335557604080516000808252602082019092529061330c565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14613516576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6133b26001600160a01b038a16612f77565b6133bc57886133d2565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106134505780518252601f199092019160209182019101613431565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d80600081146134b1576040519150601f19603f3d011682016040523d82523d6000602084013e6134b6565b606091505b5091509150816134d657604080516000808252602082019092529061330c565b8080602001905160208110156134eb57600080fd5b505192506001600160a01b03831661351357604080516000808252602082019092529061330c565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156135d957848360008151811061354957fe5b60200260200101906001600160a01b031690816001600160a01b031681525050818360018151811061357757fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c836002815181106135b957fe5b6001600160a01b039092166020928302919091019091015250612ed69050565b6001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c141561367c57731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360008151811061362057fe5b60200260200101906001600160a01b031690816001600160a01b031681525050808360018151811061364e57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505083836002815181106135b957fe5b848360008151811061368a57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505081836001815181106136b857fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c836002815181106136fa57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050808360038151811061372857fe5b60200260200101906001600160a01b031690816001600160a01b031681525050838360048151811061375657fe5b6001600160a01b0390921660209283029190910190910152505092915050565b600061378a826001600160a01b0316612f77565b156137aa5750734ddc2d193948926d02f9b1fe9e1daa0718270ed5612fae565b6001600160a01b03821660008051602061484183398151915214156137e45750735d3a536e4d6dbd6114cc1ead35777bab948e3643612fae565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef14156138245750736c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e612fae565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e8621415613864575073158079ee67fce2f58472a96584a73c7ab9ac95c1612fae565b6001600160a01b0382166000805160206148b1833981519152141561389e57507339aa39c021dfbae8fac545936693ac917d5e7563612fae565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c59914156138de575073c11b1268c1a384e55c48c2391d8d480264a3a7f4612fae565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f498141561391e575073b3319f5d18bc0d84dd1b4825dcde5d5f7266d407612fae565b6001600160a01b0382166000805160206148d18339815191521415613958575073f650c3d88d12db855b8bf7d11be6c55a4e07dcc9612fae565b506000919050565b600061396b83612f77565b1561398157506001600160a01b03811631612ed6565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156139d757600080fd5b505afa1580156139eb573d6000803e3d6000fd5b505050506040513d6020811015613a0157600080fd5b50519050612ed6565b6000613a1e826001600160a01b0316612f77565b15613a3e5750733a3a65aab0dd2a17e3f1947ba16138cd37d08c04612fae565b6001600160a01b0382166000805160206148418339815191521415613a78575073fc1e690f61efd961294b3e1ce3313fbd8aa4f85d612fae565b6001600160a01b0382166000805160206148b18339815191521415613ab25750739ba00d6856a4edf4665bca2c2309936572473b7e612fae565b6001600160a01b0382167357ab1ec28d129707052df4df418d58a2d46d5f511415613af2575073625ae63000f46200499120b906716420bd059240612fae565b6001600160a01b038216734fabb145d64652a948d72533023f6e7a623c7c531415613b325750736ee0f7bb50a54ab5253da0667b0dc2ee526c30a8612fae565b6001600160a01b0382166e085d4780b73119b644ae5ecd22b3761415613b6d5750734da9b813057d04baef4e5800e36083717b4a0341612fae565b6001600160a01b0382166000805160206148d18339815191521415613ba757507371fc860f7d3a592a4a98740e39db31d25db65ae8612fae565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415613be7575073e1ba0fb44ccb0d11b80f92f4f8ed94ca3ff51d00612fae565b6001600160a01b03821673dd974d5c2e2928dea5f71b9825b8b646686bd2001415613c275750739d91be44c06d373a8a226e1f3b146956083803eb612fae565b6001600160a01b0382167380fb784b7ed66730e8b1dbd9820afd29931aab031415613c675750737d2d3688df45ce7c552e19c27e007673da9204b8612fae565b6001600160a01b03821673514910771af9ca656af840dff83e8264ecf986ca1415613ca7575073a64bd6c70cb9051f6a9ba1f163fdc07e0dfb5f84612fae565b6001600160a01b038216730f5d2fb29fb7d3cfee444a200298f468908cc9421415613ce75750736fce4a401b6b80ace52baaefe4421bd188e76f6f612fae565b6001600160a01b038216739f8f72aa9304c8b593d555f12ef6589cc3a579a21415613d275750737deb5e830be29f91e298ba5ff1356bb7f8146998612fae565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e8621415613d6757507371010a9d003445ac60c4e6a7017c1e89a477b438612fae565b6001600160a01b03821673c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f1415613da7575073328c4c80bc7aca0834db37e6600a6c49e12da4de612fae565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415613de7575073fc4b8ed459e00e5400be803a9bb3954234fd50e3612fae565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f49814156139585750736fb0855c404e09c47c3fbca25f08d4e41f9f062f612fae565b6000613e3b846001600160a01b0316612f77565b15613ea95773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015613e8f57600080fd5b505af1158015613ea3573d6000803e3d6000fd5b50505050505b6000613ebd856001600160a01b0316612f77565b613ec75784613edd565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b90506000613ef3856001600160a01b0316612f77565b613efd5784613f13565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b6040805163e6a4390560e01b81526001600160a01b038581166004830152831660248201529051919250600091735c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f9163e6a43905916044808301926020929190829003018186803b158015613f7b57600080fd5b505afa158015613f8f573d6000803e3d6000fd5b505050506040513d6020811015613fa557600080fd5b50519050613fc46001600160a01b03821684848863ffffffff61438816565b9350613fe06001600160a01b038416828763ffffffff61443f16565b50816001600160a01b0316836001600160a01b03161015614079576040805163022c0d9f60e01b815260006004820181905260248201879052306044830152608060648301526084820181905291516001600160a01b0384169263022c0d9f9260c4808201939182900301818387803b15801561405c57600080fd5b505af1158015614070573d6000803e3d6000fd5b505050506140f3565b6040805163022c0d9f60e01b815260048101869052600060248201819052306044830152608060648301526084820181905291516001600160a01b0384169263022c0d9f9260c4808201939182900301818387803b1580156140da57600080fd5b505af11580156140ee573d6000803e3d6000fd5b505050505b614105866001600160a01b0316612f77565b156141e657604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b15801561416357600080fd5b505afa158015614177573d6000803e3d6000fd5b505050506040513d602081101561418d57600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b1580156141cd57600080fd5b505af11580156141e1573d6000803e3d6000fd5b505050505b5050509392505050565b60006142078484614202888887613e27565b613e27565b95945050505050565b6000818361429c5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614261578181015183820152602001614249565b50505050905090810190601f16801561428e5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816142a857fe5b0495945050505050565b6142bb83612f77565b61438357600081118015614349575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561431b57600080fd5b505afa15801561432f573d6000803e3d6000fd5b505050506040513d602081101561434557600080fd5b5051115b15614369576143696001600160a01b03841683600063ffffffff6144ba16565b6143836001600160a01b038416838363ffffffff6144ba16565b505050565b6000806143a46001600160a01b0386168763ffffffff61396016565b905060006143c16001600160a01b0386168863ffffffff61396016565b905060006143d7856103e563ffffffff612edc16565b905060006143eb828463ffffffff612edc16565b9050600061441183614405876103e863ffffffff612edc16565b9063ffffffff612e7916565b9050801561442e57614429828263ffffffff612f3516565b614431565b60005b9a9950505050505050505050565b60008161444e575060016114b2565b61445784612f77565b15614498576040516001600160a01b0384169083156108fc029084906000818181858888f19350505050158015614492573d6000803e3d6000fd5b506114b2565b6144b26001600160a01b038516848463ffffffff6145cd16565b5060016114b2565b801580614540575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b15801561451257600080fd5b505afa158015614526573d6000803e3d6000fd5b505050506040513d602081101561453c57600080fd5b5051155b61457b5760405162461bcd60e51b815260040180806020018281038252603681526020018061491b6036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261438390849061461b565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526143839084905b61462d826001600160a01b03166147d9565b61467e576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106146bc5780518252601f19909201916020918201910161469d565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461471e576040519150601f19603f3d011682016040523d82523d6000602084013e614723565b606091505b50915091508161477a576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b8051156147d35780806020019051602081101561479657600080fd5b50516147d35760405162461bcd60e51b815260040180806020018281038252602a8152602001806148f1602a913960400191505060405180910390fd5b50505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061480d57508115155b949350505050565b6040518061024001604052806012905b61483e8152602001906001900390816148255790505090565bfefe0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f73536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec75361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e63654f6e6553706c69743a20446973747269627574696f6e2061727261792073686f756c64206e6f74206578636565642072657365727665732061727261792073697a65a265627a7a723158207068c93019b2686c3aaa6b0508facc3360f8f4db640c51255473612168174a7c64736f6c63430005100032 \ No newline at end of file +608060405234801561001057600080fd5b5060405162004a7938038062004a798339818101604052602081101561003557600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055614a1180620000686000396000f3fe60806040526004361061041b5760003560e01c8063819faf7b1161021e578063c989b66711610123578063dc1536b2116100ab578063f4b9fa751161007a578063f4b9fa7514610ae2578063f56e281f14610af7578063f69e204614610b0c578063fa3f110b14610b21578063fbe4ed9514610b365761041b565b8063dc1536b2146109d9578063e2a7515e146109ee578063e355812314610ab8578063e44987b414610acd5761041b565b8063cede5f6a116100f2578063cede5f6a14610985578063d1aee5e314610580578063d393c3e91461099a578063d70a2d1f146109af578063d77366a4146109c45761041b565b8063c989b66714610931578063c9b42c6714610946578063cc26e9fc1461095b578063ce74b7ac146109705761041b565b8063b0a7ef29116101a6578063bf2c5a0711610175578063bf2c5a07146108c8578063c762a46c146108dd578063c77b9de6146108f2578063c7f112e414610907578063c92577751461091c5761041b565b8063b0a7ef2914610889578063b184a3ae1461089e578063b3bc7844146107f6578063b69d0456146108b35761041b565b80638ea812c0116101ed5780638ea812c014610820578063a1b4d01114610835578063a2878cb11461084a578063a4792ab31461085f578063a734f06e146108745761041b565b8063819faf7b146107cc578063851954fa146107e15780638aea49d2146107f65780638bdb2afa1461080b5761041b565b80634226a9b9116103245780635ae51b82116102ac5780636cbc4a6e1161027b5780636cbc4a6e1461076357806375a8b0121461077857806375b5be2d1461078d5780637a88bdbd146107a25780637e09b9c2146107b75761041b565b80635ae51b821461070f5780635c0cb4791461072457806364ec4e5c1461073957806368e2a0141461074e5761041b565b80634a7101d5116102f35780634a7101d5146106a65780635187c091146106bb57806351f1985c146106d057806352a701b4146106e55780635aa8fb48146106fa5761041b565b80634226a9b9146105aa578063423d03f91461066757806344211d621461067c5780634752c680146106915761041b565b80632d3b5207116103a7578063372a26cb11610376578063372a26cb146105fe5780633ca5b234146106135780633e413bee146106285780633fc8cef31461063d57806340ab7b8c146106525761041b565b80632d3b5207146105aa5780632e707bd2146105bf5780632f48ab7d146105d457806334b4dabb146105e95761041b565b806313989140116103ee57806313989140146105415780631d209b65146105565780632113240d1461056b57806321a360f51461058057806322320c98146105955761041b565b806305d8aa0a1461042a578063085e2c5b1461045157806312dea160146104fb5780631388b4201461052c575b3332141561042857600080fd5b005b34801561043657600080fd5b5061043f610b4b565b60408051918252519081900360200190f35b34801561045d57600080fd5b506104a0600480360360a081101561047457600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060800135610b52565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b838110156104e65781810151838201526020016104ce565b50505050905001935050505060405180910390f35b34801561050757600080fd5b50610510610c9e565b604080516001600160a01b039092168252519081900360200190f35b34801561053857600080fd5b50610510610cb6565b34801561054d57600080fd5b5061043f610cce565b34801561056257600080fd5b5061043f610cd4565b34801561057757600080fd5b5061043f610cdc565b34801561058c57600080fd5b5061043f610ce2565b3480156105a157600080fd5b50610510610ceb565b3480156105b657600080fd5b5061043f610d03565b3480156105cb57600080fd5b5061043f610d0c565b3480156105e057600080fd5b50610510610d11565b3480156105f557600080fd5b5061043f610d23565b34801561060a57600080fd5b50610510610d28565b34801561061f57600080fd5b50610510610d40565b34801561063457600080fd5b50610510610d58565b34801561064957600080fd5b50610510610d6a565b34801561065e57600080fd5b50610510610d82565b34801561067357600080fd5b50610510610d9a565b34801561068857600080fd5b5061043f610db2565b34801561069d57600080fd5b5061043f610db7565b3480156106b257600080fd5b5061043f610dbf565b3480156106c757600080fd5b50610510610dc4565b3480156106dc57600080fd5b50610510610ddc565b3480156106f157600080fd5b50610510610df4565b34801561070657600080fd5b5061043f610e0c565b34801561071b57600080fd5b5061043f610e12565b34801561073057600080fd5b5061043f610e18565b34801561074557600080fd5b5061043f610e1d565b34801561075a57600080fd5b5061043f610e24565b34801561076f57600080fd5b5061043f610e2b565b34801561078457600080fd5b5061043f610e32565b34801561079957600080fd5b50610510610e38565b3480156107ae57600080fd5b5061043f610e4b565b3480156107c357600080fd5b5061043f610e50565b3480156107d857600080fd5b50610510610e57565b3480156107ed57600080fd5b50610510610e6f565b34801561080257600080fd5b5061043f610e87565b34801561081757600080fd5b50610510610e90565b34801561082c57600080fd5b5061043f610ea8565b34801561084157600080fd5b50610510610eb1565b34801561085657600080fd5b5061043f610ec9565b34801561086b57600080fd5b50610510610ed1565b34801561088057600080fd5b50610510610ee9565b34801561089557600080fd5b5061043f610f01565b3480156108aa57600080fd5b50610510610f07565b3480156108bf57600080fd5b50610510610f1f565b3480156108d457600080fd5b5061043f610f37565b3480156108e957600080fd5b5061043f610f3f565b3480156108fe57600080fd5b5061043f610f44565b34801561091357600080fd5b5061043f610f4a565b34801561092857600080fd5b50610510610f52565b34801561093d57600080fd5b5061043f610f6a565b34801561095257600080fd5b5061043f610f71565b34801561096757600080fd5b5061043f610f78565b34801561097c57600080fd5b5061043f610f7d565b34801561099157600080fd5b50610510610f85565b3480156109a657600080fd5b5061043f610f9d565b3480156109bb57600080fd5b50610510610fa4565b3480156109d057600080fd5b50610510610fbc565b3480156109e557600080fd5b5061043f610fd4565b610428600480360360c0811015610a0457600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135640100000000811115610a4457600080fd5b820183602082011115610a5657600080fd5b80359060200191846020830284011164010000000083111715610a7857600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610fda915050565b348015610ac457600080fd5b5061043f61122c565b348015610ad957600080fd5b5061043f611234565b348015610aee57600080fd5b5061051061123c565b348015610b0357600080fd5b5061043f61124e565b348015610b1857600080fd5b50610510611253565b348015610b2d57600080fd5b5061043f61126b565b348015610b4257600080fd5b50610510611273565b6220000081565b600080546040805163085e2c5b60e01b81526001600160a01b03898116600483015288811660248301526044820188905260648201879052608482018690529151606093929092169163085e2c5b9160a4808201928792909190829003018186803b158015610bc057600080fd5b505afa158015610bd4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610bfd57600080fd5b815160208301805160405192949293830192919084640100000000821115610c2457600080fd5b908301906020820185811115610c3957600080fd5b8251866020820283011164010000000082111715610c5657600080fd5b82525081516020918201928201910280838360005b83811015610c83578181015183820152602001610c6b565b50505050905001604052505050915091509550959350505050565b7352ae12abe5d8bd778bd5397f99ca900624cfadd481565b73794e6e91555438afc3ccf1c5076a74f42133d08d81565b61200081565b630400000081565b61800081565b64020000000081565b73a5407eae9ba41422680e2e00537571bcc53efbfd81565b64010000000081565b608081565b60008051602061491b83398151915281565b604081565b7379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2781565b734fabb145d64652a948d72533023f6e7a623c7c5381565b6000805160206148fb83398151915281565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c81565b7345f783cce6b7ff23b2ab2d70e416cdb7d6055f5181565b601081565b631e00000081565b602081565b735c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f81565b73a2b47e3d5c44877cca798226b7b8118f9bfb7a5681565b738e870d67f660d95d5be530380d0ec0bd388289e181565b61400081565b61080081565b600881565b6202000081565b6210000081565b6208000081565b61040081565b6e085d4780b73119b644ae5ecd22b37681565b600281565b6240000081565b73398ec7346dcd622edc5ae82352f02be94c62d11981565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce31581565b64040000000081565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9581565b64080000000081565b734ddc2d193948926d02f9b1fe9e1daa0718270ed581565b634000000081565b737079e8517594e5b21d2b9a0d17cb33f5fe2bca7081565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b7306364f10b501e868329afbc005b3492902d6c76381565b7306af07097c9eeb7fd685c692751d5c66db49c21581565b630800000081565b600181565b61020081565b638000000081565b7357ab1ec28d129707052df4df418d58a2d46d5f5181565b6280000081565b6204000081565b601281565b630200000081565b7352ea46506b9cc5ef470c5bf89f17dc28bb35d85c81565b6201000081565b73f6e2d7f616b67e46d708e4410746e9aab3a4c51881565b73818e6fecd516ecc3849daf6845e3ec868087b75581565b61010081565b846001600160a01b0316866001600160a01b03161415610ff957611224565b61100161485f565b6040518061024001604052806112828152602001611503815260200161168e81526020016119b38152602001611c8c8152602001611e178152602001611fdc81526020016121f58152602001612418815260200161263b81526020016127d981526020016129858152602001612af18152602001612c398152602001612c468152602001612c688152602001612c848152602001612ca081525090506012835111156110de5760405162461bcd60e51b815260040180806020018281038252604281526020018061499b6042913960600191505060405180910390fd5b600080805b855181101561113c5760008682815181106110fa57fe5b602002602001015111156111345761112e86828151811061111757fe5b602002602001015184612ec390919063ffffffff16565b92508091505b6001016110e3565b506000821161117c5760405162461bcd60e51b815260040180806020018281038252602f8152602001806148ab602f913960400191505060405180910390fd5b8660005b865181101561121e5786818151811061119557fe5b6020026020010151600014156111aa57611216565b60006111e2856111d68a85815181106111bf57fe5b60200260200101518d612f2690919063ffffffff16565b9063ffffffff612f7f16565b9050838214156111ef5750815b80830392506112138c8c8389866012811061120657fe5b602002015163ffffffff16565b50505b600101611180565b50505050505b505050505050565b631000000081565b632000000081565b60008051602061488b83398151915281565b600481565b733d9819210a31b4961b30ef54be2aed79b9c9cd3b81565b630100000081565b6000546001600160a01b031681565b6000816112976001600160a01b038616612fc1565b6113c757604080516303795fb160e11b81526001600160a01b0387166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156112f957600080fd5b505afa15801561130d573d6000803e3d6000fd5b505050506040513d602081101561132357600080fd5b505190506001600160a01b038116156113c5576113408682612ffd565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561139657600080fd5b505af11580156113aa573d6000803e3d6000fd5b505050506040513d60208110156113c057600080fd5b505191505b505b6113d9846001600160a01b0316612fc1565b6114f957604080516303795fb160e11b81526001600160a01b0386166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b15801561143b57600080fd5b505afa15801561144f573d6000803e3d6000fd5b505050506040513d602081101561146557600080fd5b505190506001600160a01b038116156114f757806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b1580156114c757600080fd5b505af11580156114db573d6000803e3d6000fd5b50505050506040513d60208110156114f257600080fd5b505191505b505b90505b9392505050565b60006115238473818e6fecd516ecc3849daf6845e3ec868087b755612ffd565b73818e6fecd516ecc3849daf6845e3ec868087b7556329589f6161154f6001600160a01b038716612fc1565b61155a57600061155c565b835b61156e876001600160a01b0316612fc1565b611578578661158e565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b856115a1886001600160a01b0316612fc1565b6115ab57876115c1565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b15801561165957600080fd5b505af115801561166d573d6000803e3d6000fd5b50505050506040513d602081101561168457600080fd5b5051949350505050565b60006116a2846001600160a01b0316612fc1565b156117105773c0829421c1d260bd3cb3e0f06cfe2d52db2ce3156001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156116f657600080fd5b505af115801561170a573d6000803e3d6000fd5b50505050505b60007352ae12abe5d8bd778bd5397f99ca900624cfadd46001600160a01b031663bb34534c6040518163ffffffff1660e01b815260040180806c42616e636f724e6574776f726b60981b815250602001905060206040518083038186803b15801561177a57600080fd5b505afa15801561178e573d6000803e3d6000fd5b505050506040513d60208110156117a457600080fd5b5051905060606117b486866130b6565b90506117f16117cb876001600160a01b0316612fc1565b6117d557866117eb565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b83612ffd565b6000826001600160a01b031663c7ba24bc838760016040518463ffffffff1660e01b81526004018080602001848152602001838152602001828103825285818151815260200191508051906020019060200280838360005b83811015611861578181015183820152602001611849565b50505050905001945050505050602060405180830381600087803b15801561188857600080fd5b505af115801561189c573d6000803e3d6000fd5b505050506040513d60208110156118b257600080fd5b505190506118c86001600160a01b038716612fc1565b156119a957604080516370a0823160e01b8152306004820152905173c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591632e1a7d4d9183916370a08231916024808301926020929190829003018186803b15801561192657600080fd5b505afa15801561193a573d6000803e3d6000fd5b505050506040513d602081101561195057600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561199057600080fd5b505af11580156119a4573d6000803e3d6000fd5b505050505b9695505050505050565b60006119c7846001600160a01b0316612fc1565b15611a355773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015611a1b57600080fd5b505af1158015611a2f573d6000803e3d6000fd5b50505050505b611a84611a4a856001600160a01b0316612fc1565b611a545784611a6a565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b73794e6e91555438afc3ccf1c5076a74f42133d08d612ffd565b600073794e6e91555438afc3ccf1c5076a74f42133d08d630621b4f6611ab26001600160a01b038816612fc1565b611abc5786611ad2565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b85611ae5886001600160a01b0316612fc1565b611aef5787611b05565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b158015611b6357600080fd5b505af1158015611b77573d6000803e3d6000fd5b505050506040513d6020811015611b8d57600080fd5b50519050611ba36001600160a01b038516612fc1565b156114f957604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b158015611c0157600080fd5b505afa158015611c15573d6000803e3d6000fd5b505050506040513d6020811015611c2b57600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015611c6b57600080fd5b505af1158015611c7f573d6000803e3d6000fd5b5050505090509392505050565b6000806001600160a01b0385166000805160206148fb83398151915214611cb4576000611cb7565b60025b6001600160a01b03861660008051602061488b83398151915214611cdc576000611cdf565b60015b0160ff16905060006000805160206148fb8339815191526001600160a01b03861614611d0c576000611d0f565b60025b6001600160a01b03861660008051602061488b83398151915214611d34576000611d37565b60015b0160ff16905081600f0b60001480611d52575080600f0b6000145b15611d62576000925050506114fc565b611d808673a2b47e3d5c44877cca798226b7b8118f9bfb7a56612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a2b47e3d5c44877cca798226b7b8118f9bfb7a569263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b505af1158015611e0a573d6000803e3d6000fd5b5050505050509392505050565b6000806001600160a01b03851660008051602061491b83398151915214611e3f576000611e42565b60035b6001600160a01b0386166000805160206148fb83398151915214611e67576000611e6a565b60025b6001600160a01b03871660008051602061488b83398151915214611e8f576000611e92565b60015b010160ff169050600060008051602061491b8339815191526001600160a01b0316856001600160a01b031614611ec9576000611ecc565b60035b6001600160a01b0386166000805160206148fb83398151915214611ef1576000611ef4565b60025b6001600160a01b03871660008051602061488b83398151915214611f19576000611f1c565b60015b010160ff16905081600f0b60001480611f38575080600f0b6000145b15611f48576000925050506114fc565b611f66867352ea46506b9cc5ef470c5bf89f17dc28bb35d85c612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517352ea46506b9cc5ef470c5bf89f17dc28bb35d85c9263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b6000806001600160a01b0385166e085d4780b73119b644ae5ecd22b37614612005576000612008565b60045b6001600160a01b03861660008051602061491b8339815191521461202d576000612030565b60035b6001600160a01b0387166000805160206148fb83398151915214612055576000612058565b60025b6001600160a01b03881660008051602061488b8339815191521461207d576000612080565b60015b01010160ff16905060006e085d4780b73119b644ae5ecd22b3766001600160a01b0316856001600160a01b0316146120b95760006120bc565b60045b6001600160a01b03861660008051602061491b833981519152146120e15760006120e4565b60035b6001600160a01b0387166000805160206148fb8339815191521461210957600061210c565b60025b6001600160a01b03881660008051602061488b83398151915214612131576000612134565b60015b01010160ff16905081600f0b60001480612151575080600f0b6000145b15612161576000925050506114fc565b61217f867345f783cce6b7ff23b2ab2d70e416cdb7d6055f51612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517345f783cce6b7ff23b2ab2d70e416cdb7d6055f519263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b6000806001600160a01b038516734fabb145d64652a948d72533023f6e7a623c7c5314612223576000612226565b60045b6001600160a01b03861660008051602061491b8339815191521461224b57600061224e565b60035b6001600160a01b0387166000805160206148fb83398151915214612273576000612276565b60025b6001600160a01b03881660008051602061488b8339815191521461229b57600061229e565b60015b01010160ff1690506000734fabb145d64652a948d72533023f6e7a623c7c536001600160a01b0316856001600160a01b0316146122dc5760006122df565b60045b6001600160a01b03861660008051602061491b83398151915214612304576000612307565b60035b6001600160a01b0387166000805160206148fb8339815191521461232c57600061232f565b60025b6001600160a01b03881660008051602061488b83398151915214612354576000612357565b60015b01010160ff16905081600f0b60001480612374575080600f0b6000145b15612384576000925050506114fc565b6123a2867379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517379a8c46dea5ada233abaffd40f3a0a2b1e5a4f279263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b6000806001600160a01b0385167357ab1ec28d129707052df4df418d58a2d46d5f5114612446576000612449565b60045b6001600160a01b03861660008051602061491b8339815191521461246e576000612471565b60035b6001600160a01b0387166000805160206148fb83398151915214612496576000612499565b60025b6001600160a01b03881660008051602061488b833981519152146124be5760006124c1565b60015b01010160ff16905060007357ab1ec28d129707052df4df418d58a2d46d5f516001600160a01b0316856001600160a01b0316146124ff576000612502565b60045b6001600160a01b03861660008051602061491b8339815191521461252757600061252a565b60035b6001600160a01b0387166000805160206148fb8339815191521461254f576000612552565b60025b6001600160a01b03881660008051602061488b8339815191521461257757600061257a565b60015b01010160ff16905081600f0b60001480612597575080600f0b6000145b156125a7576000925050506114fc565b6125c58673a5407eae9ba41422680e2e00537571bcc53efbfd612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a5407eae9ba41422680e2e00537571bcc53efbfd9263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b600061264f846001600160a01b0316612fc1565b61270857600061265e856137c0565b905061266a8582612ffd565b806001600160a01b031663a0712d68846040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156126b057600080fd5b505af11580156126c4573d6000803e3d6000fd5b505050506040513d60208110156126da57600080fd5b50612700905081856126fb6001600160a01b0383163063ffffffff6139aa16565b611282565b9150506114fc565b61271a836001600160a01b0316612fc1565b6127cf576000612729846137c0565b90506000612738868386611282565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561278057600080fd5b505af1158015612794573d6000803e3d6000fd5b505050506040513d60208110156127aa57600080fd5b506127c690506001600160a01b0386163063ffffffff6139aa16565b925050506114fc565b5060009392505050565b60006001600160a01b03841660008051602061488b83398151915214156128bc57612818847306af07097c9eeb7fd685c692751d5c66db49c215612ffd565b60408051633b4da69f60e01b81523060048201526024810184905290517306af07097c9eeb7fd685c692751d5c66db49c21591633b4da69f91604480830192600092919082900301818387803b15801561287157600080fd5b505af1158015612885573d6000803e3d6000fd5b506128b592507306af07097c9eeb7fd685c692751d5c66db49c21591508590506126fb823063ffffffff6139aa16565b90506114fc565b6001600160a01b03831660008051602061488b83398151915214156127cf5760006128fc857306af07097c9eeb7fd685c692751d5c66db49c21585611282565b6040805163ef693bed60e01b81523060048201526024810183905290519192507306af07097c9eeb7fd685c692751d5c66db49c2159163ef693bed9160448082019260009290919082900301818387803b15801561295957600080fd5b505af115801561296d573d6000803e3d6000fd5b50612700925050506001600160a01b038516306139aa565b6000612999846001600160a01b0316612fc1565b612a575760006129a885613a54565b90506129b48582612ffd565b60408051636968703360e11b81526001600160a01b03871660048201526024810185905261044d6044820152905173398ec7346dcd622edc5ae82352f02be94c62d1199163d2d0e06691606480830192600092919082900301818387803b158015612a1e57600080fd5b505af1158015612a32573d6000803e3d6000fd5b5050505061270081856126fb30856001600160a01b03166139aa90919063ffffffff16565b612a69836001600160a01b0316612fc1565b6127cf576000612a7884613a54565b90506000612a87868386611282565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015612acf57600080fd5b505af1158015612ae3573d6000803e3d6000fd5b5050505080925050506114fc565b600080737079e8517594e5b21d2b9a0d17cb33f5fe2bca706001600160a01b031663d4b839926040518163ffffffff1660e01b815260040160206040518083038186803b158015612b4157600080fd5b505afa158015612b55573d6000803e3d6000fd5b505050506040513d6020811015612b6b57600080fd5b50519050612b798582612ffd565b806001600160a01b031663fe029156612b9a876001600160a01b0316612fc1565b612ba5576000612ba7565b845b604080516001600160e01b031960e085901b1681526001600160a01b03808b1660048301528916602482015260448101889052600060648201529051608480830192602092919082900301818588803b158015612c0357600080fd5b505af1158015612c17573d6000803e3d6000fd5b50505050506040513d6020811015612c2e57600080fd5b505195945050505050565b60006114f9848484613e71565b60006114f98473c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2858561423a565b60006114f98460008051602061488b833981519152858561423a565b60006114f9846000805160206148fb833981519152858561423a565b6000806001600160a01b038516738e870d67f660d95d5be530380d0ec0bd388289e114612cce576000612cd1565b60045b6001600160a01b03861660008051602061491b83398151915214612cf6576000612cf9565b60035b6001600160a01b0387166000805160206148fb83398151915214612d1e576000612d21565b60025b6001600160a01b03881660008051602061488b83398151915214612d46576000612d49565b60015b01010160ff1690506000738e870d67f660d95d5be530380d0ec0bd388289e16001600160a01b0316856001600160a01b031614612d87576000612d8a565b60045b6001600160a01b03861660008051602061491b83398151915214612daf576000612db2565b60035b6001600160a01b0387166000805160206148fb83398151915214612dd7576000612dda565b60025b6001600160a01b03881660008051602061488b83398151915214612dff576000612e02565b60015b01010160ff16905081600f0b60001480612e1f575080600f0b6000145b15612e2f576000925050506114fc565b612e4d867306364f10b501e868329afbc005b3492902d6c763612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517306364f10b501e868329afbc005b3492902d6c7639263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b600082820183811015612f1d576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b600082612f3557506000612f20565b82820282848281612f4257fe5b0414612f1d5760405162461bcd60e51b81526004018080602001828103825260218152602001806148da6021913960400191505060405180910390fd5b6000612f1d83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061425a565b60006001600160a01b0382161580612ff557506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b90505b919050565b61300f826001600160a01b0316612fc1565b6130b25760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b15801561306457600080fd5b505afa158015613078573d6000803e3d6000fd5b505050506040513d602081101561308e57600080fd5b5051901c6130b2576130b26001600160a01b0383168260001963ffffffff6142fc16565b5050565b6060816001600160a01b0316836001600160a01b031614156130e75750604080516000815260208101909152612f20565b6130f9836001600160a01b0316612fc1565b156131165773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31592505b613128826001600160a01b0316612fc1565b156131455773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591505b6001600160a01b038316731f573d6fb3f13d689ff844b4ce37794d79a7ff1c148061318c57506001600160a01b038216731f573d6fb3f13d689ff844b4ce37794d79a7ff1c145b156131b7576040805160038082526080820190925290602082016060803883390190505090506131d9565b60408051600580825260c08201909252906020820160a0803883390190505090505b6000806001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c146133a2576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6132366001600160a01b038b16612fc1565b6132405789613256565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106132d45780518252601f1990920191602091820191016132b5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613335576040519150601f19603f3d011682016040523d82523d6000602084013e61333a565b606091505b5091509150816133625760408051600080825260208201909252905b50945050505050612f20565b80806020019051602081101561337757600080fd5b505193506001600160a01b03841661339f576040805160008082526020820190925290613356565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14613560576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6133fc6001600160a01b038a16612fc1565b613406578861341c565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b6020831061349a5780518252601f19909201916020918201910161347b565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d80600081146134fb576040519150601f19603f3d011682016040523d82523d6000602084013e613500565b606091505b509150915081613520576040805160008082526020820190925290613356565b80806020019051602081101561353557600080fd5b505192506001600160a01b03831661355d576040805160008082526020820190925290613356565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c141561362357848360008151811061359357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505081836001815181106135c157fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360028151811061360357fe5b6001600160a01b039092166020928302919091019091015250612f209050565b6001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156136c657731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360008151811061366a57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050808360018151811061369857fe5b60200260200101906001600160a01b031690816001600160a01b031681525050838360028151811061360357fe5b84836000815181106136d457fe5b60200260200101906001600160a01b031690816001600160a01b031681525050818360018151811061370257fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360028151811061374457fe5b60200260200101906001600160a01b031690816001600160a01b031681525050808360038151811061377257fe5b60200260200101906001600160a01b031690816001600160a01b03168152505083836004815181106137a057fe5b6001600160a01b0390921660209283029190910190910152505092915050565b60006137d4826001600160a01b0316612fc1565b156137f45750734ddc2d193948926d02f9b1fe9e1daa0718270ed5612ff8565b6001600160a01b03821660008051602061488b833981519152141561382e5750735d3a536e4d6dbd6114cc1ead35777bab948e3643612ff8565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef141561386e5750736c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e612ff8565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e86214156138ae575073158079ee67fce2f58472a96584a73c7ab9ac95c1612ff8565b6001600160a01b0382166000805160206148fb83398151915214156138e857507339aa39c021dfbae8fac545936693ac917d5e7563612ff8565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415613928575073c11b1268c1a384e55c48c2391d8d480264a3a7f4612ff8565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415613968575073b3319f5d18bc0d84dd1b4825dcde5d5f7266d407612ff8565b6001600160a01b03821660008051602061491b83398151915214156139a2575073f650c3d88d12db855b8bf7d11be6c55a4e07dcc9612ff8565b506000919050565b60006139b583612fc1565b156139cb57506001600160a01b03811631612f20565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015613a2157600080fd5b505afa158015613a35573d6000803e3d6000fd5b505050506040513d6020811015613a4b57600080fd5b50519050612f20565b6000613a68826001600160a01b0316612fc1565b15613a885750733a3a65aab0dd2a17e3f1947ba16138cd37d08c04612ff8565b6001600160a01b03821660008051602061488b8339815191521415613ac2575073fc1e690f61efd961294b3e1ce3313fbd8aa4f85d612ff8565b6001600160a01b0382166000805160206148fb8339815191521415613afc5750739ba00d6856a4edf4665bca2c2309936572473b7e612ff8565b6001600160a01b0382167357ab1ec28d129707052df4df418d58a2d46d5f511415613b3c575073625ae63000f46200499120b906716420bd059240612ff8565b6001600160a01b038216734fabb145d64652a948d72533023f6e7a623c7c531415613b7c5750736ee0f7bb50a54ab5253da0667b0dc2ee526c30a8612ff8565b6001600160a01b0382166e085d4780b73119b644ae5ecd22b3761415613bb75750734da9b813057d04baef4e5800e36083717b4a0341612ff8565b6001600160a01b03821660008051602061491b8339815191521415613bf157507371fc860f7d3a592a4a98740e39db31d25db65ae8612ff8565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415613c31575073e1ba0fb44ccb0d11b80f92f4f8ed94ca3ff51d00612ff8565b6001600160a01b03821673dd974d5c2e2928dea5f71b9825b8b646686bd2001415613c715750739d91be44c06d373a8a226e1f3b146956083803eb612ff8565b6001600160a01b0382167380fb784b7ed66730e8b1dbd9820afd29931aab031415613cb15750737d2d3688df45ce7c552e19c27e007673da9204b8612ff8565b6001600160a01b03821673514910771af9ca656af840dff83e8264ecf986ca1415613cf1575073a64bd6c70cb9051f6a9ba1f163fdc07e0dfb5f84612ff8565b6001600160a01b038216730f5d2fb29fb7d3cfee444a200298f468908cc9421415613d315750736fce4a401b6b80ace52baaefe4421bd188e76f6f612ff8565b6001600160a01b038216739f8f72aa9304c8b593d555f12ef6589cc3a579a21415613d715750737deb5e830be29f91e298ba5ff1356bb7f8146998612ff8565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e8621415613db157507371010a9d003445ac60c4e6a7017c1e89a477b438612ff8565b6001600160a01b03821673c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f1415613df1575073328c4c80bc7aca0834db37e6600a6c49e12da4de612ff8565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415613e31575073fc4b8ed459e00e5400be803a9bb3954234fd50e3612ff8565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f49814156139a25750736fb0855c404e09c47c3fbca25f08d4e41f9f062f612ff8565b6000613e85846001600160a01b0316612fc1565b15613ef35773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015613ed957600080fd5b505af1158015613eed573d6000803e3d6000fd5b50505050505b6000613f07856001600160a01b0316612fc1565b613f115784613f27565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b90506000613f3d856001600160a01b0316612fc1565b613f475784613f5d565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b6040805163e6a4390560e01b81526001600160a01b038581166004830152831660248201529051919250600091735c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f9163e6a43905916044808301926020929190829003018186803b158015613fc557600080fd5b505afa158015613fd9573d6000803e3d6000fd5b505050506040513d6020811015613fef57600080fd5b5051905061400e6001600160a01b03821684848863ffffffff6143d216565b935061402a6001600160a01b038416828763ffffffff61448916565b50816001600160a01b0316836001600160a01b031610156140c3576040805163022c0d9f60e01b815260006004820181905260248201879052306044830152608060648301526084820181905291516001600160a01b0384169263022c0d9f9260c4808201939182900301818387803b1580156140a657600080fd5b505af11580156140ba573d6000803e3d6000fd5b5050505061413d565b6040805163022c0d9f60e01b815260048101869052600060248201819052306044830152608060648301526084820181905291516001600160a01b0384169263022c0d9f9260c4808201939182900301818387803b15801561412457600080fd5b505af1158015614138573d6000803e3d6000fd5b505050505b61414f866001600160a01b0316612fc1565b1561423057604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b1580156141ad57600080fd5b505afa1580156141c1573d6000803e3d6000fd5b505050506040513d60208110156141d757600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561421757600080fd5b505af115801561422b573d6000803e3d6000fd5b505050505b5050509392505050565b6000614251848461424c888887613e71565b613e71565b95945050505050565b600081836142e65760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156142ab578181015183820152602001614293565b50505050905090810190601f1680156142d85780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816142f257fe5b0495945050505050565b61430583612fc1565b6143cd57600081118015614393575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561436557600080fd5b505afa158015614379573d6000803e3d6000fd5b505050506040513d602081101561438f57600080fd5b5051115b156143b3576143b36001600160a01b03841683600063ffffffff61450416565b6143cd6001600160a01b038416838363ffffffff61450416565b505050565b6000806143ee6001600160a01b0386168763ffffffff6139aa16565b9050600061440b6001600160a01b0386168863ffffffff6139aa16565b90506000614421856103e563ffffffff612f2616565b90506000614435828463ffffffff612f2616565b9050600061445b8361444f876103e863ffffffff612f2616565b9063ffffffff612ec316565b9050801561447857614473828263ffffffff612f7f16565b61447b565b60005b9a9950505050505050505050565b600081614498575060016114fc565b6144a184612fc1565b156144e2576040516001600160a01b0384169083156108fc029084906000818181858888f193505050501580156144dc573d6000803e3d6000fd5b506114fc565b6144fc6001600160a01b038516848463ffffffff61461716565b5060016114fc565b80158061458a575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b15801561455c57600080fd5b505afa158015614570573d6000803e3d6000fd5b505050506040513d602081101561458657600080fd5b5051155b6145c55760405162461bcd60e51b81526004018080602001828103825260368152602001806149656036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526143cd908490614665565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526143cd9084905b614677826001600160a01b0316614823565b6146c8576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106147065780518252601f1990920191602091820191016146e7565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114614768576040519150601f19603f3d011682016040523d82523d6000602084013e61476d565b606091505b5091509150816147c4576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b80511561481d578080602001905160208110156147e057600080fd5b505161481d5760405162461bcd60e51b815260040180806020018281038252602a81526020018061493b602a913960400191505060405180910390fd5b50505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061485757508115155b949350505050565b6040518061024001604052806012905b61488881526020019060019003908161486f5790505090565bfefe0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f73536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec75361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e63654f6e6553706c69743a20446973747269627574696f6e2061727261792073686f756c64206e6f74206578636565642072657365727665732061727261792073697a65a265627a7a72315820dc9f329eb1e422ec1b4a38de19717cceb09997b1c5b7a3470705e669c2ab2da064736f6c63430005110032 \ No newline at end of file diff --git a/OneSplit.full.sol b/OneSplit.full.sol index 6ba7e25..e1bf74d 100644 --- a/OneSplit.full.sol +++ b/OneSplit.full.sol @@ -139,6 +139,10 @@ contract IOneSplitConsts { uint256 public constant FLAG_DISABLE_ALL_SPLIT_SOURCES = 0x20000000; uint256 public constant FLAG_DISABLE_ALL_WRAP_SOURCES = 0x40000000; uint256 public constant FLAG_DISABLE_CURVE_PAX = 0x80000000; + uint256 public constant FLAG_DISABLE_UNISWAP_POOL_TOKEN = 0x100000000; + uint256 public constant FLAG_DISABLE_BALANCER_POOL_TOKEN = 0x200000000; + uint256 public constant FLAG_DISABLE_CURVE_ZAP = 0x400000000; + uint256 public constant FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN = 0x800000000; } @@ -353,6 +357,10 @@ interface IUniswapExchange { uint256 deadline, address tokenAddr ) external returns (uint256 tokensBought); + + function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline) external payable returns (uint256); + + function removeLiquidity(uint256 amount, uint256 min_eth, uint256 min_tokens, uint256 deadline) external returns (uint256, uint256); } // File: contracts/interface/IUniswapFactory.sol @@ -363,6 +371,8 @@ pragma solidity ^0.5.0; interface IUniswapFactory { function getExchange(IERC20 token) external view returns (IUniswapExchange exchange); + + function getToken(address exchange) external view returns (IERC20 token); } // File: contracts/interface/IKyberNetworkContract.sol @@ -539,8 +549,14 @@ interface ICurve { // solium-disable-next-line mixedcase function get_dy_underlying(int128 i, int128 j, uint256 dx) external view returns(uint256 dy); + function get_virtual_price() external view returns(uint256); + // solium-disable-next-line mixedcase function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 minDy) external; + + function coins(int128 arg0) external view returns (address); + + function balances(int128 arg0) external view returns (uint256); } // File: contracts/interface/IChai.sol @@ -4143,42 +4159,107 @@ contract OneSplitWeth is OneSplitBaseWrap { } } -// File: contracts/OneSplit.sol +// File: contracts/interface/IBFactory.sol pragma solidity ^0.5.0; +interface IBFactory { + function isBPool(address b) external view returns (bool); +} +// File: contracts/interface/IBPool.sol +pragma solidity ^0.5.0; +contract BConst { + uint public constant EXIT_FEE = 0; +} +contract IBMath is BConst { + function calcPoolOutGivenSingleIn( + uint tokenBalanceIn, + uint tokenWeightIn, + uint poolSupply, + uint totalWeight, + uint tokenAmountIn, + uint swapFee + ) + public + pure returns (uint poolAmountOut); +} +contract IBPool is IERC20, IBMath { + function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn) external; -//import "./OneSplitSmartToken.sol"; + function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut) external; + function joinswapExternAmountIn(address tokenIn, uint tokenAmountIn, uint minPoolAmountOut) external returns (uint poolAmountOut); -contract OneSplitViewWrap is - OneSplitViewWrapBase, - OneSplitMultiPathView, - OneSplitChaiView, - OneSplitBdaiView, - OneSplitAaveView, - OneSplitFulcrumView, - OneSplitCompoundView, - OneSplitIearnView, - OneSplitIdleView, - OneSplitWethView - //OneSplitSmartTokenView -{ - IOneSplitView public oneSplitView; + function getCurrentTokens() external view returns (address[] memory tokens); - constructor(IOneSplitView _oneSplit) public { - oneSplitView = _oneSplit; + function getBalance(address token) external view returns (uint); + + function getNormalizedWeight(address token) external view returns (uint); + + function getDenormalizedWeight(address token) external view returns (uint); + + function getTotalDenormalizedWeight() external view returns (uint); + + function getSwapFee() external view returns (uint); +} + +// File: contracts/OneSplitBalancerPoolToken.sol + +pragma solidity ^0.5.0; + + + + + +contract OneSplitBalancerPoolTokenBase { + using SafeMath for uint256; + + // todo: factory for Bronze release + // may be changed in future + IBFactory bFactory = IBFactory(0x9424B1412450D0f8Fc2255FAf6046b98213B76Bd); + + struct TokenWithWeight { + IERC20 token; + uint256 reserveBalance; + uint256 denormalizedWeight; + } + + struct PoolTokenDetails { + TokenWithWeight[] tokens; + uint256 totalWeight; + uint256 totalSupply; + } + + function _getPoolDetails(IBPool poolToken) + internal + view + returns(PoolTokenDetails memory details) + { + address[] memory currentTokens = poolToken.getCurrentTokens(); + details.tokens = new TokenWithWeight[](currentTokens.length); + details.totalWeight = poolToken.getTotalDenormalizedWeight(); + details.totalSupply = poolToken.totalSupply(); + for (uint256 i = 0; i < details.tokens.length; i++) { + details.tokens[i].token = IERC20(currentTokens[i]); + details.tokens[i].denormalizedWeight = poolToken.getDenormalizedWeight(currentTokens[i]); + details.tokens[i].reserveBalance = poolToken.getBalance(currentTokens[i]); + } } +} + + +contract OneSplitBalancerPoolTokenView is OneSplitViewWrapBase, OneSplitBalancerPoolTokenBase { + function getExpectedReturn( IERC20 fromToken, IERC20 toToken, @@ -4188,7 +4269,7 @@ contract OneSplitViewWrap is ) public view - returns( + returns ( uint256 returnAmount, uint256[] memory distribution ) @@ -4197,6 +4278,62 @@ contract OneSplitViewWrap is return (amount, new uint256[](DEXES_COUNT)); } + + if (!flags.check(FLAG_DISABLE_BALANCER_POOL_TOKEN)) { + bool isPoolTokenFrom = bFactory.isBPool(address(fromToken)); + bool isPoolTokenTo = bFactory.isBPool(address(toToken)); + + if (isPoolTokenFrom && isPoolTokenTo) { + ( + uint256 returnETHAmount, + uint256[] memory poolTokenFromDistribution + ) = _getExpectedReturnFromBalancerPoolToken( + fromToken, + ETH_ADDRESS, + amount, + parts, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + + ( + uint256 returnPoolTokenToAmount, + uint256[] memory poolTokenToDistribution + ) = _getExpectedReturnToBalancerPoolToken( + ETH_ADDRESS, + toToken, + returnETHAmount, + parts, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + + for (uint i = 0; i < poolTokenToDistribution.length; i++) { + poolTokenFromDistribution[i] |= poolTokenToDistribution[i] << 128; + } + + return (returnPoolTokenToAmount, poolTokenFromDistribution); + } + + if (isPoolTokenFrom) { + return _getExpectedReturnFromBalancerPoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _getExpectedReturnToBalancerPoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + } + } + return super.getExpectedReturn( fromToken, toToken, @@ -4206,96 +4343,2562 @@ contract OneSplitViewWrap is ); } - function _getExpectedReturnFloor( - IERC20 fromToken, + function _getExpectedReturnFromBalancerPoolToken( + IERC20 poolToken, IERC20 toToken, uint256 amount, uint256 parts, uint256 flags ) - internal + private view - returns( + returns ( uint256 returnAmount, uint256[] memory distribution ) { - return oneSplitView.getExpectedReturn( - fromToken, - toToken, - amount, - parts, - flags + distribution = new uint256[](DEXES_COUNT); + + IBPool bToken = IBPool(address(poolToken)); + address[] memory currentTokens = bToken.getCurrentTokens(); + + uint256 pAiAfterExitFee = amount.sub( + amount.mul(bToken.EXIT_FEE()) ); - } -} + uint256 ratio = pAiAfterExitFee.mul(1e18).div(poolToken.totalSupply()); + for (uint i = 0; i < currentTokens.length; i++) { + uint256 tokenAmountOut = bToken.getBalance(currentTokens[i]).mul(ratio).div(1e18); + if (currentTokens[i] == address(toToken)) { + returnAmount = returnAmount.add(tokenAmountOut); + continue; + } -contract OneSplitWrap is - OneSplitBaseWrap, - OneSplitMultiPath, - OneSplitChai, - OneSplitBdai, - OneSplitAave, - OneSplitFulcrum, - OneSplitCompound, - OneSplitIearn, - OneSplitIdle, - OneSplitWeth - //OneSplitSmartToken -{ - IOneSplitView public oneSplitView; - IOneSplit public oneSplit; + (uint256 ret, uint256[] memory dist) = getExpectedReturn( + IERC20(currentTokens[i]), + toToken, + tokenAmountOut, + parts, + flags + ); - constructor(IOneSplitView _oneSplitView, IOneSplit _oneSplit) public { - oneSplitView = _oneSplitView; - oneSplit = _oneSplit; - } + returnAmount = returnAmount.add(ret); - function() external payable { - // solium-disable-next-line security/no-tx-origin - require(msg.sender != tx.origin); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + return (returnAmount, distribution); } - function getExpectedReturn( + function _getExpectedReturnToBalancerPoolToken( IERC20 fromToken, - IERC20 toToken, + IERC20 poolToken, uint256 amount, uint256 parts, - uint256 flags // 1 - Uniswap, 2 - Kyber, 4 - Bancor, 8 - Oasis, 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI + uint256 flags ) - public + private view - returns( - uint256 /*returnAmount*/, - uint256[] memory /*distribution*/ + returns ( + uint256 minFundAmount, + uint256[] memory distribution ) { - return oneSplitView.getExpectedReturn( + distribution = new uint256[](DEXES_COUNT); + minFundAmount = uint256(-1); + + PoolTokenDetails memory details = _getPoolDetails(IBPool(address(poolToken))); + + uint256[] memory tokenAmounts = new uint256[](details.tokens.length); + uint256[] memory dist; + uint256[] memory fundAmounts = new uint256[](details.tokens.length); + + for (uint i = 0; i < details.tokens.length; i++) { + uint256 exchangeAmount = amount.mul( + details.tokens[i].denormalizedWeight + ).div(details.totalWeight); + + if (details.tokens[i].token != fromToken) { + (tokenAmounts[i], dist) = getExpectedReturn( + fromToken, + details.tokens[i].token, + exchangeAmount, + parts, + flags + ); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } else { + tokenAmounts[i] = exchangeAmount; + } + + fundAmounts[i] = tokenAmounts[i] + .mul(details.totalSupply) + .div(details.tokens[i].reserveBalance); + + if (fundAmounts[i] < minFundAmount) { + minFundAmount = fundAmounts[i]; + } + } + +// uint256 _minFundAmount = minFundAmount; +// uint256 swapFee = IBPool(address(poolToken)).getSwapFee(); + // Swap leftovers for PoolToken +// for (uint i = 0; i < details.tokens.length; i++) { +// if (_minFundAmount == fundAmounts[i]) { +// continue; +// } +// +// uint256 leftover = tokenAmounts[i].sub( +// fundAmounts[i].mul(details.tokens[i].reserveBalance).div(details.totalSupply) +// ); +// +// uint256 tokenRet = IBPool(address(poolToken)).calcPoolOutGivenSingleIn( +// details.tokens[i].reserveBalance, +// details.tokens[i].denormalizedWeight, +// details.totalSupply, +// details.totalWeight, +// leftover, +// swapFee +// ); +// +// minFundAmount = minFundAmount.add(tokenRet); +// } + + return (minFundAmount, distribution); + } + +} + + +contract OneSplitBalancerPoolToken is OneSplitBaseWrap, OneSplitBalancerPoolTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) internal { + if (fromToken == toToken) { + return; + } + + if (!flags.check(FLAG_DISABLE_BALANCER_POOL_TOKEN)) { + bool isPoolTokenFrom = bFactory.isBPool(address(fromToken)); + bool isPoolTokenTo = bFactory.isBPool(address(toToken)); + + if (isPoolTokenFrom && isPoolTokenTo) { + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] & ((1 << 128) - 1); + } + + uint256 ethBalanceBefore = address(this).balance; + + _swapFromBalancerPoolToken( + fromToken, + ETH_ADDRESS, + amount, + dist, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] >> 128; + } + + uint256 ethBalanceAfter = address(this).balance; + + return _swapToBalancerPoolToken( + ETH_ADDRESS, + toToken, + ethBalanceAfter.sub(ethBalanceBefore), + dist, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + } + + if (isPoolTokenFrom) { + return _swapFromBalancerPoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _swapToBalancerPoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_BALANCER_POOL_TOKEN + ); + } + } + + return super._swap( fromToken, toToken, amount, - parts, + distribution, flags ); } - function swap( - IERC20 fromToken, + function _swapFromBalancerPoolToken( + IERC20 poolToken, IERC20 toToken, uint256 amount, - uint256 minReturn, - uint256[] memory distribution, // [Uniswap, Kyber, Bancor, Oasis] - uint256 flags // 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI - ) public payable { - fromToken.universalTransferFrom(msg.sender, address(this), amount); + uint256[] memory distribution, + uint256 flags + ) private { - _swap(fromToken, toToken, amount, distribution, flags); + IBPool bToken = IBPool(address(poolToken)); - uint256 returnAmount = toToken.universalBalanceOf(address(this)); - require(returnAmount >= minReturn, "OneSplit: actual return amount is less than minReturn"); - toToken.universalTransfer(msg.sender, returnAmount); - fromToken.universalTransfer(msg.sender, fromToken.universalBalanceOf(address(this))); + address[] memory currentTokens = bToken.getCurrentTokens(); + + uint256 ratio = amount.sub( + amount.mul(bToken.EXIT_FEE()) + ).mul(1e18).div(poolToken.totalSupply()); + + uint256[] memory minAmountsOut = new uint256[](currentTokens.length); + for (uint i = 0; i < currentTokens.length; i++) { + minAmountsOut[i] = bToken.getBalance(currentTokens[i]).mul(ratio).div(1e18).mul(995).div(1000); // 0.5% slippage; + } + + bToken.exitPool(amount, minAmountsOut); + + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < currentTokens.length; i++) { + + if (currentTokens[i] == address(toToken)) { + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + uint256 exchangeTokenAmount = IERC20(currentTokens[i]).balanceOf(address(this)); + + this.swap( + IERC20(currentTokens[i]), + toToken, + exchangeTokenAmount, + 0, + dist, + flags + ); + } + + } + + function _swapToBalancerPoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + uint256[] memory dist = new uint256[](distribution.length); + uint256 minFundAmount = uint256(-1); + + PoolTokenDetails memory details = _getPoolDetails(IBPool(address(poolToken))); + + uint256[] memory maxAmountsIn = new uint256[](details.tokens.length); + uint256 curFundAmount; + for (uint i = 0; i < details.tokens.length; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].denormalizedWeight) + .div(details.totalWeight); + + if (details.tokens[i].token != fromToken) { + uint256 tokenBalanceBefore = details.tokens[i].token.balanceOf(address(this)); + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + this.swap( + fromToken, + details.tokens[i].token, + exchangeAmount, + 0, + dist, + flags + ); + + uint256 tokenBalanceAfter = details.tokens[i].token.balanceOf(address(this)); + + curFundAmount = ( + tokenBalanceAfter.sub(tokenBalanceBefore) + ).mul(details.totalSupply).div(details.tokens[i].reserveBalance); + } else { + curFundAmount = ( + exchangeAmount + ).mul(details.totalSupply).div(details.tokens[i].reserveBalance); + } + + if (curFundAmount < minFundAmount) { + minFundAmount = curFundAmount; + } + + maxAmountsIn[i] = uint256(-1); + _infiniteApproveIfNeeded(details.tokens[i].token, address(poolToken)); + } + + // todo: check for vulnerability + IBPool(address(poolToken)).joinPool(minFundAmount, maxAmountsIn); + + // Return leftovers + for (uint i = 0; i < details.tokens.length; i++) { + details.tokens[i].token.universalTransfer(msg.sender, details.tokens[i].token.balanceOf(address(this))); + } + } +} + +// File: contracts/OneSplitUniswapPoolToken.sol + +pragma solidity ^0.5.0; + + + + + +contract OneSplitUniswapPoolTokenBase { + using SafeMath for uint256; + + IUniswapFactory constant uniswapFactory = IUniswapFactory(0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95); + + function isLiquidityPool(IERC20 token) internal view returns (bool) { + return address(uniswapFactory.getToken(address(token))) != address(0); + } + + function getMaxPossibleFund( + IERC20 poolToken, + IERC20 uniswapToken, + uint256 tokenAmount, + uint256 existEthAmount + ) + internal + view + returns ( + uint256, + uint256 + ) + { + uint256 ethReserve = address(poolToken).balance; + uint256 totalLiquidity = poolToken.totalSupply(); + uint256 tokenReserve = uniswapToken.balanceOf(address(poolToken)); + + uint256 possibleEthAmount = ethReserve.mul( + tokenAmount.sub(1) + ).div(tokenReserve); + + if (existEthAmount > possibleEthAmount) { + return ( + possibleEthAmount, + possibleEthAmount.mul(totalLiquidity).div(ethReserve) + ); + } + + return ( + existEthAmount, + existEthAmount.mul(totalLiquidity).div(ethReserve) + ); + } + +} + + +contract OneSplitUniswapPoolTokenView is OneSplitViewWrapBase, OneSplitUniswapPoolTokenBase { + + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + public + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + if (fromToken == toToken) { + return (amount, new uint256[](DEXES_COUNT)); + } + + + if (!flags.check(FLAG_DISABLE_UNISWAP_POOL_TOKEN)) { + bool isPoolTokenFrom = isLiquidityPool(fromToken); + bool isPoolTokenTo = isLiquidityPool(toToken); + + if (isPoolTokenFrom && isPoolTokenTo) { + ( + uint256 returnETHAmount, + uint256[] memory poolTokenFromDistribution + ) = _getExpectedReturnFromPoolToken( + fromToken, + ETH_ADDRESS, + amount, + parts, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + + ( + uint256 returnPoolTokenToAmount, + uint256[] memory poolTokenToDistribution + ) = _getExpectedReturnToPoolToken( + ETH_ADDRESS, + toToken, + returnETHAmount, + parts, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + + for (uint i = 0; i < poolTokenToDistribution.length; i++) { + poolTokenFromDistribution[i] |= poolTokenToDistribution[i] << 128; + } + + return (returnPoolTokenToAmount, poolTokenFromDistribution); + } + + if (isPoolTokenFrom) { + return _getExpectedReturnFromPoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _getExpectedReturnToPoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + } + + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + flags + ); + } + + function _getExpectedReturnFromPoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + + distribution = new uint256[](DEXES_COUNT); + + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + + uint256 totalSupply = poolToken.totalSupply(); + + uint256 ethReserve = address(poolToken).balance; + uint256 ethAmount = amount.mul(ethReserve).div(totalSupply); + + if (!toToken.isETH()) { + (uint256 ret, uint256[] memory dist) = getExpectedReturn( + ETH_ADDRESS, + toToken, + ethAmount, + parts, + flags + ); + + returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j]; + } + } else { + returnAmount = returnAmount.add(ethAmount); + } + + uint256 tokenReserve = uniswapToken.balanceOf(address(poolToken)); + uint256 exchangeTokenAmount = amount.mul(tokenReserve).div(totalSupply); + + if (toToken != uniswapToken) { + (uint256 ret, uint256[] memory dist) = getExpectedReturn( + uniswapToken, + toToken, + exchangeTokenAmount, + parts, + flags + ); + + returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << 8; + } + } else { + returnAmount = returnAmount.add(exchangeTokenAmount); + } + + return (returnAmount, distribution); + } + + function _getExpectedReturnToPoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + + distribution = new uint256[](DEXES_COUNT); + + uint256[] memory dist = new uint256[](DEXES_COUNT); + + uint256 ethAmount; + uint256 partAmountForEth = amount.div(2); + if (!fromToken.isETH()) { + (ethAmount, dist) = super.getExpectedReturn( + fromToken, + ETH_ADDRESS, + partAmountForEth, + parts, + flags + ); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j]; + } + } else { + ethAmount = partAmountForEth; + } + + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + + uint256 tokenAmount; + uint256 partAmountForToken = amount.sub(partAmountForEth); + if (fromToken != uniswapToken) { + (tokenAmount, dist) = super.getExpectedReturn( + fromToken, + uniswapToken, + partAmountForToken, + parts, + flags + ); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << 8; + } + } else { + tokenAmount = partAmountForToken; + } + + (, returnAmount) = getMaxPossibleFund( + poolToken, + uniswapToken, + tokenAmount, + ethAmount + ); + + return ( + returnAmount, + distribution + ); + } + +} + + +contract OneSplitUniswapPoolToken is OneSplitBaseWrap, OneSplitUniswapPoolTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) internal { + if (fromToken == toToken) { + return; + } + + if (!flags.check(FLAG_DISABLE_UNISWAP_POOL_TOKEN)) { + bool isPoolTokenFrom = isLiquidityPool(fromToken); + bool isPoolTokenTo = isLiquidityPool(toToken); + + if (isPoolTokenFrom && isPoolTokenTo) { + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] & ((1 << 128) - 1); + } + + uint256 ethBalanceBefore = address(this).balance; + + _swapFromPoolToken( + fromToken, + ETH_ADDRESS, + amount, + dist, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] >> 128; + } + + uint256 ethBalanceAfter = address(this).balance; + + return _swapToPoolToken( + ETH_ADDRESS, + toToken, + ethBalanceAfter.sub(ethBalanceBefore), + dist, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + + if (isPoolTokenFrom) { + return _swapFromPoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _swapToPoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_UNISWAP_POOL_TOKEN + ); + } + } + + return super._swap( + fromToken, + toToken, + amount, + distribution, + flags + ); + } + + function _swapFromPoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + + uint256[] memory dist = new uint256[](distribution.length); + + ( + uint256 ethAmount, + uint256 exchangeTokenAmount + ) = IUniswapExchange(address(poolToken)).removeLiquidity( + amount, + 1, + 1, + now.add(1800) + ); + + if (!toToken.isETH()) { + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j]) & 0xFF; + } + + super._swap( + ETH_ADDRESS, + toToken, + ethAmount, + dist, + flags + ); + } + + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + + if (toToken != uniswapToken) { + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> 8) & 0xFF; + } + + super._swap( + uniswapToken, + toToken, + exchangeTokenAmount, + dist, + flags + ); + } + } + + function _swapToPoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + uint256[] memory dist = new uint256[](distribution.length); + + uint256 partAmountForEth = amount.div(2); + if (!fromToken.isETH()) { + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j]) & 0xFF; + } + + super._swap( + fromToken, + ETH_ADDRESS, + partAmountForEth, + dist, + flags + ); + } + + IERC20 uniswapToken = uniswapFactory.getToken(address(poolToken)); + + uint256 partAmountForToken = amount.sub(partAmountForEth); + if (fromToken != uniswapToken) { + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> 8) & 0xFF; + } + + super._swap( + fromToken, + uniswapToken, + partAmountForToken, + dist, + flags + ); + + _infiniteApproveIfNeeded(uniswapToken, address(poolToken)); + } + + uint256 ethBalance = address(this).balance; + uint256 tokenBalance = uniswapToken.balanceOf(address(this)); + + (uint256 ethAmount, uint256 returnAmount) = getMaxPossibleFund( + poolToken, + uniswapToken, + tokenBalance, + ethBalance + ); + + IUniswapExchange(address(poolToken)).addLiquidity.value(ethAmount)( + returnAmount.mul(995).div(1000), // 0.5% slippage + uint256(-1), // todo: think about another value + now.add(1800) + ); + + // todo: do we need to check difference between balance before and balance after? + uniswapToken.universalTransfer(msg.sender, uniswapToken.balanceOf(address(this))); + ETH_ADDRESS.universalTransfer(msg.sender, address(this).balance); + } +} + +// File: contracts/OneSplitCurvePoolToken.sol + +pragma solidity ^0.5.0; + + + + +contract OneSplitCurvePoolTokenBase { + using SafeMath for uint256; + using UniversalERC20 for IERC20; + + IERC20 constant curveSusdToken = IERC20(0xC25a3A3b969415c80451098fa907EC722572917F); + IERC20 constant curveIearnToken = IERC20(0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8); + IERC20 constant curveCompoundToken = IERC20(0x845838DF265Dcd2c412A1Dc9e959c7d08537f8a2); + IERC20 constant curveUsdtToken = IERC20(0x9fC689CCaDa600B6DF723D9E47D84d76664a1F23); + IERC20 constant curveBinanceToken = IERC20(0x3B3Ac5386837Dc563660FB6a0937DFAa5924333B); + IERC20 constant curvePaxToken = IERC20(0xD905e2eaeBe188fc92179b6350807D8bd91Db0D8); + IERC20 constant curveRenBtcToken = IERC20(0x7771F704490F9C0C3B06aFe8960dBB6c58CBC812); + IERC20 constant curveTBtcToken = IERC20(0x1f2a662FB513441f06b8dB91ebD9a1466462b275); + + ICurve constant curveSusd = ICurve(0xA5407eAE9Ba41422680e2e00537571bcC53efBfD); + ICurve constant curveIearn = ICurve(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); + ICurve constant curveCompound = ICurve(0xA2B47E3D5c44877cca798226B7B8118F9BFb7A56); + ICurve constant curveUsdt = ICurve(0x52EA46506B9CC5Ef470C5bf89f17Dc28bB35D85C); + ICurve constant curveBinance = ICurve(0x79a8C46DeA5aDa233ABaFFD40F3A0A2B1e5A4F27); + ICurve constant curvePax = ICurve(0x06364f10B501e868329afBc005b3492902d6C763); + ICurve constant curveRenBtc = ICurve(0x8474c1236F0Bc23830A23a41aBB81B2764bA9f4F); + ICurve constant curveTBtc = ICurve(0x9726e9314eF1b96E45f40056bEd61A088897313E); + + struct CurveTokenInfo { + IERC20 token; + uint256 weightedReserveBalance; + } + + struct CurveInfo { + ICurve curve; + uint256 tokenCount; + } + + struct CurvePoolTokenDetails { + CurveTokenInfo[] tokens; + uint256 totalWeightedBalance; + } + + function _isPoolToken(IERC20 token) + internal + pure + returns (bool) + { + if ( + token == curveSusdToken || + token == curveIearnToken || + token == curveCompoundToken || + token == curveUsdtToken || + token == curveBinanceToken || + token == curvePaxToken || + token == curveRenBtcToken || + token == curveTBtcToken + ) { + return true; + } + return false; + } + + function _getCurve(IERC20 poolToken) + internal + pure + returns (CurveInfo memory curveInfo) + { + if (poolToken == curveSusdToken) { + curveInfo.curve = curveSusd; + curveInfo.tokenCount = 4; + return curveInfo; + } + + if (poolToken == curveIearnToken) { + curveInfo.curve = curveIearn; + curveInfo.tokenCount = 4; + return curveInfo; + } + + if (poolToken == curveCompoundToken) { + curveInfo.curve = curveCompound; + curveInfo.tokenCount = 2; + return curveInfo; + } + + if (poolToken == curveUsdtToken) { + curveInfo.curve = curveUsdt; + curveInfo.tokenCount = 3; + return curveInfo; + } + + if (poolToken == curveBinanceToken) { + curveInfo.curve = curveBinance; + curveInfo.tokenCount = 4; + return curveInfo; + } + + if (poolToken == curvePaxToken) { + curveInfo.curve = curvePax; + curveInfo.tokenCount = 4; + return curveInfo; + } + + if (poolToken == curveRenBtcToken) { + curveInfo.curve = curveRenBtc; + curveInfo.tokenCount = 2; + return curveInfo; + } + + if (poolToken == curveTBtcToken) { + curveInfo.curve = curveTBtc; + curveInfo.tokenCount = 3; + return curveInfo; + } + + revert(); + } + + function _getCurveCalcTokenAmountSelector(uint256 tokenCount) + internal + pure + returns (bytes4) + { + return bytes4(keccak256(abi.encodePacked( + "calc_token_amount(uint256[", uint8(48 + tokenCount) ,"],bool)" + ))); + } + + function _getCurveRemoveLiquiditySelector(uint256 tokenCount) + internal + pure + returns (bytes4) + { + return bytes4(keccak256(abi.encodePacked( + "remove_liquidity(uint256,uint256[", uint8(48 + tokenCount) ,"])" + ))); + } + + function _getCurveAddLiquiditySelector(uint256 tokenCount) + internal + pure + returns (bytes4) + { + return bytes4(keccak256(abi.encodePacked( + "add_liquidity(uint256[", uint8(48 + tokenCount) ,"],uint256)" + ))); + } + + function _getPoolDetails(ICurve curve, uint256 tokenCount) + internal + view + returns(CurvePoolTokenDetails memory details) + { + details.tokens = new CurveTokenInfo[](tokenCount); + for (uint256 i = 0; i < tokenCount; i++) { + details.tokens[i].token = IERC20(curve.coins(int128(i))); + details.tokens[i].weightedReserveBalance = curve.balances(int128(i)) + .mul(1e18).div(10 ** details.tokens[i].token.universalDecimals()); + details.totalWeightedBalance = details.totalWeightedBalance.add( + details.tokens[i].weightedReserveBalance + ); + } + } +} + + +contract OneSplitCurvePoolTokenView is OneSplitViewWrapBase, OneSplitCurvePoolTokenBase { + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + public + view + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) + { + if (fromToken == toToken) { + return (amount, new uint256[](DEXES_COUNT)); + } + + + if (!flags.check(FLAG_DISABLE_CURVE_ZAP)) { + if (_isPoolToken(fromToken)) { + return _getExpectedReturnFromCurvePoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_CURVE_ZAP + ); + } + + if (_isPoolToken(toToken)) { + return _getExpectedReturnToCurvePoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_CURVE_ZAP + ); + } + } + + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + flags + ); + } + + function _getExpectedReturnFromCurvePoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + CurveInfo memory curveInfo = _getCurve(poolToken); + uint256 totalSupply = poolToken.totalSupply(); + for (uint i = 0; i < curveInfo.tokenCount; i++) { + IERC20 coin = IERC20(curveInfo.curve.coins(int128(i))); + + uint256 tokenAmountOut = curveInfo.curve.balances(int128(i)) + .mul(amount) + .div(totalSupply); + + if (coin == toToken) { + returnAmount = returnAmount.add(tokenAmountOut); + continue; + } + + (uint256 ret, uint256[] memory dist) = this.getExpectedReturn( + coin, + toToken, + tokenAmountOut, + parts, + flags + ); + + returnAmount = returnAmount.add(ret); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + return (returnAmount, distribution); + } + + function _getExpectedReturnToCurvePoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns ( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + CurveInfo memory curveInfo = _getCurve(poolToken); + CurvePoolTokenDetails memory details = _getPoolDetails( + curveInfo.curve, + curveInfo.tokenCount + ); + + bytes memory tokenAmounts; + for (uint i = 0; i < curveInfo.tokenCount; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].weightedReserveBalance) + .div(details.totalWeightedBalance); + + if (details.tokens[i].token == fromToken) { + tokenAmounts = abi.encodePacked(tokenAmounts, exchangeAmount); + continue; + } + + (uint256 tokenAmount, uint256[] memory dist) = this.getExpectedReturn( + fromToken, + details.tokens[i].token, + exchangeAmount, + parts, + flags + ); + + tokenAmounts = abi.encodePacked(tokenAmounts, tokenAmount); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + (bool success, bytes memory data) = address(curveInfo.curve).staticcall( + abi.encodePacked( + _getCurveCalcTokenAmountSelector(curveInfo.tokenCount), + tokenAmounts, + uint256(1) + ) + ); + + require(success, "calc_token_amount failed"); + + return (abi.decode(data, (uint256)), distribution); + } +} + + +contract OneSplitCurvePoolToken is OneSplitBaseWrap, OneSplitCurvePoolTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) internal { + if (fromToken == toToken) { + return; + } + + if (!flags.check(FLAG_DISABLE_CURVE_ZAP)) { + if (_isPoolToken(fromToken)) { + return _swapFromCurvePoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_CURVE_ZAP + ); + } + + if (_isPoolToken(toToken)) { + return _swapToCurvePoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_CURVE_ZAP + ); + } + } + + return super._swap( + fromToken, + toToken, + amount, + distribution, + flags + ); + } + + function _swapFromCurvePoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + CurveInfo memory curveInfo = _getCurve(poolToken); + + bytes memory minAmountsOut; + for (uint i = 0; i < curveInfo.tokenCount; i++) { + minAmountsOut = abi.encodePacked(minAmountsOut, uint256(1)); + } + + (bool success,) = address(curveInfo.curve).call( + abi.encodePacked( + _getCurveRemoveLiquiditySelector(curveInfo.tokenCount), + amount, + minAmountsOut + ) + ); + + require(success, "remove_liquidity failed"); + + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < curveInfo.tokenCount; i++) { + IERC20 coin = IERC20(curveInfo.curve.coins(int128(i))); + + if (coin == toToken) { + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + uint256 exchangeTokenAmount = coin.universalBalanceOf(address(this)); + + this.swap( + coin, + toToken, + exchangeTokenAmount, + 0, + dist, + flags + ); + } + } + + function _swapToCurvePoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + uint256[] memory dist = new uint256[](distribution.length); + + CurveInfo memory curveInfo = _getCurve(poolToken); + CurvePoolTokenDetails memory details = _getPoolDetails( + curveInfo.curve, + curveInfo.tokenCount + ); + + bytes memory tokenAmounts; + for (uint i = 0; i < curveInfo.tokenCount; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].weightedReserveBalance) + .div(details.totalWeightedBalance); + + _infiniteApproveIfNeeded(details.tokens[i].token, address(curveInfo.curve)); + + if (details.tokens[i].token == fromToken) { + tokenAmounts = abi.encodePacked(tokenAmounts, exchangeAmount); + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + this.swap( + fromToken, + details.tokens[i].token, + exchangeAmount, + 0, + dist, + flags + ); + + tokenAmounts = abi.encodePacked( + tokenAmounts, + details.tokens[i].token.universalBalanceOf(address(this)) + ); + } + + (bool success,) = address(curveInfo.curve).call( + abi.encodePacked( + _getCurveAddLiquiditySelector(curveInfo.tokenCount), + tokenAmounts, + uint256(0) + ) + ); + + require(success, "add_liquidity failed"); + } +} + +// File: contracts/interface/ISmartTokenConverter.sol + +pragma solidity ^0.5.0; + + +interface ISmartTokenConverter { + + function version() external view returns (uint16); + + function connectors(address) external view returns (uint256, uint32, bool, bool, bool); + + function getReserveRatio(IERC20 token) external view returns (uint256); + + function connectorTokenCount() external view returns (uint256); + + function connectorTokens(uint256 i) external view returns (IERC20); + + function liquidate(uint256 _amount) external; + + function fund(uint256 _amount) external; + + function convert2(IERC20 _fromToken, IERC20 _toToken, uint256 _amount, uint256 _minReturn, address _affiliateAccount, uint256 _affiliateFee) external returns (uint256); + + function convert(IERC20 _fromToken, IERC20 _toToken, uint256 _amount, uint256 _minReturn) external returns (uint256); + +} + +// File: contracts/interface/ISmartToken.sol + +pragma solidity ^0.5.0; + + + + +interface ISmartToken { + function owner() external view returns (ISmartTokenConverter); +} + +// File: contracts/interface/ISmartTokenRegistry.sol + +pragma solidity ^0.5.0; + + + +interface ISmartTokenRegistry { + function isSmartToken(IERC20 token) external view returns (bool); +} + +// File: contracts/interface/ISmartTokenFormula.sol + +pragma solidity ^0.5.0; + + + +interface ISmartTokenFormula { + function calculateLiquidateReturn( + uint256 supply, + uint256 reserveBalance, + uint32 totalRatio, + uint256 amount + ) external view returns (uint256); + + function calculatePurchaseReturn( + uint256 supply, + uint256 reserveBalance, + uint32 totalRatio, + uint256 amount + ) external view returns (uint256); +} + +// File: contracts/OneSplitSmartToken.sol + +pragma solidity ^0.5.0; + + + + + + + +contract OneSplitSmartTokenBase { + using SafeMath for uint256; + + ISmartTokenRegistry constant smartTokenRegistry = ISmartTokenRegistry(0xf6E2D7F616B67E46D708e4410746E9AAb3a4C518); + ISmartTokenFormula constant smartTokenFormula = ISmartTokenFormula(0x524619EB9b4cdFFa7DA13029b33f24635478AFc0); + IERC20 constant bntToken = IERC20(0x1F573D6Fb3F13d689FF844B4cE37794d79a7FF1C); + IERC20 constant usdbToken = IERC20(0x309627af60F0926daa6041B8279484312f2bf060); + + IERC20 constant susd = IERC20(0x57Ab1ec28D129707052df4dF418D58a2D46d5f51); + IERC20 constant acientSUSD = IERC20(0x57Ab1E02fEE23774580C119740129eAC7081e9D3); + + struct TokenWithRatio { + IERC20 token; + uint256 ratio; + } + + struct SmartTokenDetails { + TokenWithRatio[] tokens; + address converter; + uint256 totalRatio; + } + + function _getSmartTokenDetails(ISmartToken smartToken) + internal + view + returns(SmartTokenDetails memory details) + { + ISmartTokenConverter converter = smartToken.owner(); + details.converter = address(converter); + details.tokens = new TokenWithRatio[](converter.connectorTokenCount()); + + for (uint256 i = 0; i < details.tokens.length; i++) { + details.tokens[i].token = converter.connectorTokens(i); + details.tokens[i].ratio = _getReserveRatio(converter, details.tokens[i].token); + details.totalRatio = details.totalRatio.add(details.tokens[i].ratio); + } + } + + function _getReserveRatio( + ISmartTokenConverter converter, + IERC20 token + ) + internal + view + returns (uint256) + { + (bool success, bytes memory data) = address(converter).staticcall.gas(10000)( + abi.encodeWithSelector( + converter.getReserveRatio.selector, + token + ) + ); + + if (!success) { + (, uint32 ratio, , ,) = converter.connectors(address(token)); + + return uint256(ratio); + } + + return abi.decode(data, (uint256)); + } + + function _canonicalSUSD(IERC20 token) internal pure returns(IERC20) { + return token == acientSUSD ? susd : token; + } +} + + +contract OneSplitSmartTokenView is OneSplitViewWrapBase, OneSplitSmartTokenBase { + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + public + view + returns( + uint256, + uint256[] memory + ) + { + if (fromToken == toToken) { + return (amount, new uint256[](DEXES_COUNT)); + } + + if (!flags.check(FLAG_DISABLE_SMART_TOKEN)) { + bool isSmartTokenFrom = smartTokenRegistry.isSmartToken(fromToken); + bool isSmartTokenTo = smartTokenRegistry.isSmartToken(toToken); + + if (isSmartTokenFrom && isSmartTokenTo) { + ( + uint256 returnBntAmount, + uint256[] memory smartTokenFromDistribution + ) = _getExpectedReturnFromSmartToken( + fromToken, + bntToken, + amount, + parts, + FLAG_DISABLE_SMART_TOKEN + ); + + ( + uint256 returnSmartTokenToAmount, + uint256[] memory smartTokenToDistribution + ) = _getExpectedReturnToSmartToken( + bntToken, + toToken, + returnBntAmount, + parts, + FLAG_DISABLE_SMART_TOKEN + ); + + for (uint i = 0; i < smartTokenToDistribution.length; i++) { + smartTokenFromDistribution[i] |= smartTokenToDistribution[i] << 128; + } + + return (returnSmartTokenToAmount, smartTokenFromDistribution); + } + + if (isSmartTokenFrom) { + return _getExpectedReturnFromSmartToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_SMART_TOKEN + ); + } + + if (isSmartTokenTo) { + return _getExpectedReturnToSmartToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_SMART_TOKEN + ); + } + } + + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + flags + ); + } + + function _getExpectedReturnFromSmartToken( + IERC20 smartToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); + + for (uint i = 0; i < details.tokens.length; i++) { + uint256 srcAmount = smartTokenFormula.calculateLiquidateReturn( + smartToken.totalSupply(), + _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), + uint32(details.totalRatio), + amount + ); + + if (details.tokens[i].token == toToken) { + returnAmount = returnAmount.add(srcAmount); + continue; + } + + (uint256 ret, uint256[] memory dist) = this.getExpectedReturn( + _canonicalSUSD(details.tokens[i].token), + toToken, + srcAmount, + parts, + flags + ); + + returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + return (returnAmount, distribution); + } + + function _getExpectedReturnToSmartToken( + IERC20 fromToken, + IERC20 smartToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns( + uint256 minFundAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + minFundAmount = uint256(-1); + + SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); + + uint256[] memory tokenAmounts = new uint256[](details.tokens.length); + uint256[] memory dist; + uint256[] memory fundAmounts = new uint256[](details.tokens.length); + + for (uint i = 0; i < details.tokens.length; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].ratio) + .div(details.totalRatio); + + if (details.tokens[i].token != fromToken) { + (tokenAmounts[i], dist) = this.getExpectedReturn( + fromToken, + _canonicalSUSD(details.tokens[i].token), + exchangeAmount, + parts, + flags + ); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } else { + tokenAmounts[i] = exchangeAmount; + } + + fundAmounts[i] = smartTokenFormula.calculatePurchaseReturn( + smartToken.totalSupply(), + _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), + uint32(details.totalRatio), + tokenAmounts[i] + ); + + if (fundAmounts[i] < minFundAmount) { + minFundAmount = fundAmounts[i]; + } + } + + return (minFundAmount, distribution); + } +} + + +contract OneSplitSmartToken is OneSplitBaseWrap, OneSplitSmartTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) internal { + if (fromToken == toToken) { + return; + } + + if (!flags.check(FLAG_DISABLE_SMART_TOKEN)) { + + bool isSmartTokenFrom = smartTokenRegistry.isSmartToken(fromToken); + bool isSmartTokenTo = smartTokenRegistry.isSmartToken(toToken); + + if (isSmartTokenFrom && isSmartTokenTo) { + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] & ((1 << 128) - 1); + } + + uint256 bntBalanceBefore = bntToken.balanceOf(address(this)); + + _swapFromSmartToken( + fromToken, + bntToken, + amount, + dist, + FLAG_DISABLE_SMART_TOKEN + ); + + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] >> 128; + } + + uint256 bntBalanceAfter = bntToken.balanceOf(address(this)); + + return _swapToSmartToken( + bntToken, + toToken, + bntBalanceAfter.sub(bntBalanceBefore), + dist, + FLAG_DISABLE_SMART_TOKEN + ); + } + + if (isSmartTokenFrom) { + return _swapFromSmartToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_SMART_TOKEN + ); + } + + if (isSmartTokenTo) { + return _swapToSmartToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_SMART_TOKEN + ); + } + } + + return super._swap( + fromToken, + toToken, + amount, + distribution, + flags + ); + } + + function _swapFromSmartToken( + IERC20 smartToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); + + ISmartTokenConverter(details.converter).liquidate(amount); + + uint256[] memory dist = new uint256[](distribution.length); + + for (uint i = 0; i < details.tokens.length; i++) { + if (details.tokens[i].token == toToken) { + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + this.swap( + _canonicalSUSD(details.tokens[i].token), + toToken, + _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)), + 0, + dist, + flags + ); + } + } + + function _swapToSmartToken( + IERC20 fromToken, + IERC20 smartToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + + uint256[] memory dist = new uint256[](distribution.length); + uint256 minFundAmount = uint256(-1); + + SmartTokenDetails memory details = _getSmartTokenDetails(ISmartToken(address(smartToken))); + + uint256 curFundAmount; + for (uint i = 0; i < details.tokens.length; i++) { + uint256 exchangeAmount = amount + .mul(details.tokens[i].ratio) + .div(details.totalRatio); + + if (details.tokens[i].token != fromToken) { + + uint256 tokenBalanceBefore = _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)); + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + this.swap( + fromToken, + _canonicalSUSD(details.tokens[i].token), + exchangeAmount, + 0, + dist, + flags + ); + + uint256 tokenBalanceAfter = _canonicalSUSD(details.tokens[i].token).balanceOf(address(this)); + + curFundAmount = smartTokenFormula.calculatePurchaseReturn( + smartToken.totalSupply(), + _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), + uint32(details.totalRatio), + tokenBalanceAfter.sub(tokenBalanceBefore) + ); + } else { + curFundAmount = smartTokenFormula.calculatePurchaseReturn( + smartToken.totalSupply(), + _canonicalSUSD(details.tokens[i].token).balanceOf(details.converter), + uint32(details.totalRatio), + exchangeAmount + ); + } + + if (curFundAmount < minFundAmount) { + minFundAmount = curFundAmount; + } + + _infiniteApproveIfNeeded(_canonicalSUSD(details.tokens[i].token), details.converter); + } + + ISmartTokenConverter(details.converter).fund(minFundAmount); + + for (uint i = 0; i < details.tokens.length; i++) { + IERC20 reserveToken = _canonicalSUSD(details.tokens[i].token); + reserveToken.universalTransfer( + msg.sender, + reserveToken.universalBalanceOf(address(this)) + ); + } + } +} + +// File: contracts/interface/IUniswapV2Router.sol + +pragma solidity ^0.5.0; + + +interface IUniswapV2Router { + function addLiquidity( + IERC20 tokenA, + IERC20 tokenB, + uint amountADesired, + uint amountBDesired, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) external returns (uint256[2] memory amounts, uint liquidity); + + function removeLiquidity( + IERC20 tokenA, + IERC20 tokenB, + uint liquidity, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) external returns (uint256[2] memory); + + function swapExactTokensForTokens( + uint amountIn, + uint amountOutMin, + IERC20[] calldata path, + address to, + uint deadline + ) external returns (uint[] memory amounts); + + function getAmountsOut(uint amountIn, IERC20[] calldata path) external view returns (uint[] memory amounts); +} + +// File: contracts/interface/IUniswapV2Pair.sol + +pragma solidity ^0.5.0; + + +interface IUniswapV2Pair { + function factory() external view returns (address); + + function token0() external view returns (IERC20); + function token1() external view returns (IERC20); + + function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); + + function mint(address to) external returns (uint liquidity); + function burn(address to) external returns (uint amount0, uint amount1); +} + +// File: contracts/OneSplitUniswapV2PoolToken.sol + +pragma solidity ^0.5.0; + + + + + +library Math { + function min(uint x, uint y) internal pure returns (uint z) { + z = x < y ? x : y; + } + + // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) + function sqrt(uint y) internal pure returns (uint z) { + if (y > 3) { + z = y; + uint x = y / 2 + 1; + while (x < z) { + z = x; + x = (y / x + x) / 2; + } + } else if (y != 0) { + z = 1; + } + } +} + + +contract OneSplitUniswapV2PoolTokenBase { + using SafeMath for uint256; + + IUniswapV2Router constant uniswapRouter = IUniswapV2Router(0xf164fC0Ec4E93095b804a4795bBe1e041497b92a); + + function isLiquidityPool(IERC20 token) internal view returns (bool) { + (bool success, bytes memory data) = address(token).staticcall.gas(2000)( + abi.encode(IUniswapV2Pair(address(token)).factory.selector) + ); + if (!success || data.length == 0) { + return false; + } + return abi.decode(data, (address)) == 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; + } + + struct TokenInfo { + IERC20 token; + uint256 reserve; + } + + struct PoolDetails { + TokenInfo[2] tokens; + uint256 totalSupply; + } + + function _getPoolDetails(IUniswapV2Pair pair) internal view returns (PoolDetails memory details) { + (uint112 reserve0, uint112 reserve1, ) = pair.getReserves(); + + details.tokens[0] = TokenInfo({ + token: pair.token0(), + reserve: reserve0 + }); + details.tokens[1] = TokenInfo({ + token: pair.token1(), + reserve: reserve1 + }); + + details.totalSupply = IERC20(address(pair)).totalSupply(); + } + + function _calcRebalanceAmount( + uint256 leftover, + uint256 balanceOfLeftoverAsset, + uint256 secondAssetBalance + ) internal pure returns (uint256) { + + return Math.sqrt( + 3988000 * leftover * balanceOfLeftoverAsset + + 3988009 * balanceOfLeftoverAsset * balanceOfLeftoverAsset - + 9 * balanceOfLeftoverAsset * balanceOfLeftoverAsset / (secondAssetBalance - 1) + ) / 1994 - balanceOfLeftoverAsset * 1997 / 1994; + } + +} + + +contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswapV2PoolTokenBase { + + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + public + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + if (fromToken == toToken) { + return (amount, new uint256[](DEXES_COUNT)); + } + + + if (!flags.check(FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN)) { + bool isPoolTokenFrom = isLiquidityPool(fromToken); + bool isPoolTokenTo = isLiquidityPool(toToken); + + if (isPoolTokenFrom && isPoolTokenTo) { + ( + uint256 returnWETHAmount, + uint256[] memory poolTokenFromDistribution + ) = _getExpectedReturnFromUniswapV2PoolToken( + fromToken, + weth, + amount, + parts, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + + ( + uint256 returnPoolTokenToAmount, + uint256[] memory poolTokenToDistribution + ) = _getExpectedReturnToUniswapV2PoolToken( + weth, + toToken, + returnWETHAmount, + parts, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + + for (uint i = 0; i < poolTokenToDistribution.length; i++) { + poolTokenFromDistribution[i] |= poolTokenToDistribution[i] << 128; + } + + return (returnPoolTokenToAmount, poolTokenFromDistribution); + } + + if (isPoolTokenFrom) { + return _getExpectedReturnFromUniswapV2PoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _getExpectedReturnToUniswapV2PoolToken( + fromToken, + toToken, + amount, + parts, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + } + + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + flags + ); + } + + function _getExpectedReturnFromUniswapV2PoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); + + for (uint i = 0; i < 2; i++) { + + uint256 exchangeAmount = amount + .mul(details.tokens[i].reserve) + .div(details.totalSupply); + + if (toToken == details.tokens[i].token) { + returnAmount = returnAmount.add(exchangeAmount); + continue; + } + + (uint256 ret, uint256[] memory dist) = this.getExpectedReturn( + details.tokens[i].token, + toToken, + exchangeAmount, + parts, + flags + ); + + returnAmount = returnAmount.add(ret); + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + return (returnAmount, distribution); + } + + function _getExpectedReturnToUniswapV2PoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + private + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + distribution = new uint256[](DEXES_COUNT); + + PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); + + // will overwritten to liquidity amounts + uint256[2] memory amounts; + amounts[0] = amount.div(2); + amounts[1] = amount.sub(amounts[0]); + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < 2; i++) { + + if (fromToken == details.tokens[i].token) { + continue; + } + + (amounts[i], dist) = this.getExpectedReturn( + fromToken, + details.tokens[i].token, + amounts[i], + parts, + flags + ); + + for (uint j = 0; j < distribution.length; j++) { + distribution[j] |= dist[j] << (i * 8); + } + } + + uint256 possibleLiquidity0 = amounts[0].mul(details.totalSupply).div(details.tokens[0].reserve); + returnAmount = Math.min( + possibleLiquidity0, + amounts[1].mul(details.totalSupply).div(details.tokens[1].reserve) + ); + + uint256 leftoverIndex = possibleLiquidity0 > returnAmount ? 0 : 1; + IERC20[] memory path = new IERC20[](2); + path[0] = details.tokens[leftoverIndex].token; + path[1] = details.tokens[1 - leftoverIndex].token; + + uint256 optimalAmount = amounts[1 - leftoverIndex].mul( + details.tokens[leftoverIndex].reserve + ) / details.tokens[1 - leftoverIndex].reserve; + + IERC20 _poolToken = poolToken; // stack too deep + uint256 exchangeAmount = _calcRebalanceAmount( + amounts[leftoverIndex].sub(optimalAmount), + path[0].balanceOf(address(_poolToken)).add(optimalAmount), + path[1].balanceOf(address(_poolToken)).add(amounts[1 - leftoverIndex]) + ); + + (bool success, bytes memory data) = address(uniswapRouter).staticcall.gas(100000)( + abi.encodeWithSelector( + uniswapRouter.getAmountsOut.selector, + exchangeAmount, + path + ) + ); + + if (!success) { + return ( + returnAmount, + distribution + ); + } + + uint256[] memory amountsOutAfterSwap = abi.decode(data, (uint256[])); + + uint256 _addedLiquidity = returnAmount; // stack too deep + PoolDetails memory _details = details; // stack too deep + returnAmount = _addedLiquidity.add( + amountsOutAfterSwap[1] // amountOut after swap + .mul(_details.totalSupply.add(_addedLiquidity)) + .div(_details.tokens[leftoverIndex].reserve.sub(amountsOutAfterSwap[1])) + ); + + return ( + returnAmount, + distribution + ); + } + +} + + +contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTokenBase { + function _swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) internal { + if (fromToken == toToken) { + return; + } + + if (!flags.check(FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN)) { + bool isPoolTokenFrom = isLiquidityPool(fromToken); + bool isPoolTokenTo = isLiquidityPool(toToken); + + if (isPoolTokenFrom && isPoolTokenTo) { + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] & ((1 << 128) - 1); + } + + uint256 wEthBalanceBefore = weth.balanceOf(address(this)); + + _swapFromUniswapV2PoolToken( + fromToken, + weth, + amount, + dist, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + + for (uint i = 0; i < distribution.length; i++) { + dist[i] = distribution[i] >> 128; + } + + uint256 wEthBalanceAfter = weth.balanceOf(address(this)); + + return _swapToUniswapV2PoolToken( + weth, + toToken, + wEthBalanceAfter.sub(wEthBalanceBefore), + dist, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + + if (isPoolTokenFrom) { + return _swapFromUniswapV2PoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + + if (isPoolTokenTo) { + return _swapToUniswapV2PoolToken( + fromToken, + toToken, + amount, + distribution, + FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN + ); + } + } + + return super._swap( + fromToken, + toToken, + amount, + distribution, + flags + ); + } + + function _swapFromUniswapV2PoolToken( + IERC20 poolToken, + IERC20 toToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + _infiniteApproveIfNeeded(poolToken, address(uniswapRouter)); + + IERC20 [2] memory tokens = [ + IUniswapV2Pair(address(poolToken)).token0(), + IUniswapV2Pair(address(poolToken)).token1() + ]; + + uint256[2] memory amounts = uniswapRouter.removeLiquidity( + tokens[0], + tokens[1], + amount, + uint256(0), + uint256(0), + address(this), + now.add(1800) + ); + + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < 2; i++) { + + if (toToken == tokens[i]) { + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + this.swap( + tokens[i], + toToken, + amounts[i], + 0, + dist, + flags + ); + } + } + + function _swapToUniswapV2PoolToken( + IERC20 fromToken, + IERC20 poolToken, + uint256 amount, + uint256[] memory distribution, + uint256 flags + ) private { + IERC20 [2] memory tokens = [ + IUniswapV2Pair(address(poolToken)).token0(), + IUniswapV2Pair(address(poolToken)).token1() + ]; + + // will overwritten to liquidity amounts + uint256[2] memory amounts; + amounts[0] = amount.div(2); + amounts[1] = amount.sub(amounts[0]); + uint256[] memory dist = new uint256[](distribution.length); + for (uint i = 0; i < 2; i++) { + + _infiniteApproveIfNeeded(tokens[i], address(uniswapRouter)); + + if (fromToken == tokens[i]) { + continue; + } + + for (uint j = 0; j < distribution.length; j++) { + dist[j] = (distribution[j] >> (i * 8)) & 0xFF; + } + + this.swap( + fromToken, + tokens[i], + amounts[i], + 0, + dist, + flags + ); + + amounts[i] = tokens[i].universalBalanceOf(address(this)); + } + + (uint256[2] memory redeemAmounts, ) = uniswapRouter.addLiquidity( + tokens[0], + tokens[1], + amounts[0], + amounts[1], + uint256(0), + uint256(0), + address(this), + now.add(1800) + ); + + if (redeemAmounts[0] == amounts[0]) { + return; + } + + uint256 leftoverIndex = amounts[0] != redeemAmounts[0] ? 0 : 1; + IERC20[] memory path = new IERC20[](2); + path[0] = tokens[leftoverIndex]; + path[1] = tokens[1 - leftoverIndex]; + + uint256 exchangeAmount = _calcRebalanceAmount( + amounts[leftoverIndex].sub(redeemAmounts[leftoverIndex]), + path[0].balanceOf(address(poolToken)), + path[1].balanceOf(address(poolToken)) + ); + + (bool success, bytes memory data) = address(uniswapRouter).call.gas(1000000)( + abi.encodeWithSelector( + uniswapRouter.swapExactTokensForTokens.selector, + exchangeAmount, + uint256(0), + path, + address(this), + now.add(1800) + ) + ); + + if (!success) { + return; + } + + uint256[] memory amountsOut = abi.decode(data, (uint256[])); + + address(uniswapRouter).call.gas(1000000)( + abi.encodeWithSelector( + uniswapRouter.addLiquidity.selector, + tokens[0], + tokens[1], + amountsOut[leftoverIndex], + amountsOut[1 - leftoverIndex], + uint256(0), + uint256(0), + address(this), + now.add(1800) + ) + ); + } +} + +// File: contracts/OneSplit.sol + +pragma solidity ^0.5.0; + + + + + + + + + + + + + + + + + + +contract OneSplitViewWrap is + OneSplitViewWrapBase, + OneSplitMultiPathView, + OneSplitChaiView, + OneSplitBdaiView, + OneSplitAaveView, + OneSplitFulcrumView, + OneSplitCompoundView, + OneSplitIearnView, + OneSplitIdleView, + OneSplitWethView, + //OneSplitBalancerPoolTokenView, + //OneSplitUniswapPoolTokenView, + //OneSplitCurvePoolTokenView + //OneSplitSmartTokenView, + OneSplitUniswapV2PoolTokenView +{ + IOneSplitView public oneSplitView; + + constructor(IOneSplitView _oneSplit) public { + oneSplitView = _oneSplit; + } + + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + public + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + if (fromToken == toToken) { + return (amount, new uint256[](DEXES_COUNT)); + } + + return super.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + flags + ); + } + + function _getExpectedReturnFloor( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags + ) + internal + view + returns( + uint256 returnAmount, + uint256[] memory distribution + ) + { + return oneSplitView.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + flags + ); + } +} + + +contract OneSplitWrap is + OneSplitBaseWrap, + //OneSplitMultiPath, + OneSplitChai, + OneSplitBdai, + OneSplitAave, +// OneSplitFulcrum, + OneSplitCompound, + OneSplitIearn, + OneSplitIdle, + OneSplitWeth, + //OneSplitBalancerPoolToken, + //OneSplitUniswapPoolToken, + //OneSplitCurvePoolToken + //OneSplitSmartToken, + OneSplitUniswapV2PoolToken +{ + IOneSplitView public oneSplitView; + IOneSplit public oneSplit; + + constructor(IOneSplitView _oneSplitView, IOneSplit _oneSplit) public { + oneSplitView = _oneSplitView; + oneSplit = _oneSplit; + } + + function() external payable { + // solium-disable-next-line security/no-tx-origin + require(msg.sender != tx.origin); + } + + function getExpectedReturn( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 parts, + uint256 flags // 1 - Uniswap, 2 - Kyber, 4 - Bancor, 8 - Oasis, 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI + ) + public + view + returns( + uint256 /*returnAmount*/, + uint256[] memory /*distribution*/ + ) + { + return oneSplitView.getExpectedReturn( + fromToken, + toToken, + amount, + parts, + flags + ); + } + + function swap( + IERC20 fromToken, + IERC20 toToken, + uint256 amount, + uint256 minReturn, + uint256[] memory distribution, // [Uniswap, Kyber, Bancor, Oasis] + uint256 flags // 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI + ) public payable { + if (msg.sender != address(this)) { + fromToken.universalTransferFrom(msg.sender, address(this), amount); + } + + _swap(fromToken, toToken, amount, distribution, flags); + + uint256 returnAmount = toToken.universalBalanceOf(address(this)); + require(returnAmount >= minReturn, "OneSplit: actual return amount is less than minReturn"); + + if (msg.sender != address(this)) { + toToken.universalTransfer(msg.sender, returnAmount); + fromToken.universalTransfer(msg.sender, fromToken.universalBalanceOf(address(this))); + } } function _swapFloor( diff --git a/contracts/IOneSplit.sol b/contracts/IOneSplit.sol index e0f85eb..acbd813 100644 --- a/contracts/IOneSplit.sol +++ b/contracts/IOneSplit.sol @@ -64,6 +64,7 @@ contract IOneSplitConsts { uint256 public constant FLAG_DISABLE_UNISWAP_V2_POOL_TOKEN = 0x800000000; } + contract IOneSplit is IOneSplitConsts { function getExpectedReturn( IERC20 fromToken, diff --git a/contracts/OneSplit.sol b/contracts/OneSplit.sol index 04ef3ec..a9640f6 100644 --- a/contracts/OneSplit.sol +++ b/contracts/OneSplit.sol @@ -21,19 +21,19 @@ import "./OneSplitUniswapV2PoolToken.sol"; contract OneSplitViewWrap is OneSplitViewWrapBase, OneSplitMultiPathView, - //OneSplitChaiView, - //OneSplitBdaiView, - //OneSplitAaveView, - //OneSplitFulcrumView, + OneSplitChaiView, + OneSplitBdaiView, + OneSplitAaveView, + OneSplitFulcrumView, OneSplitCompoundView, OneSplitIearnView, - //OneSplitIdleView, + OneSplitIdleView, OneSplitWethView, //OneSplitBalancerPoolTokenView, //OneSplitUniswapPoolTokenView, - OneSplitCurvePoolTokenView + //OneSplitCurvePoolTokenView //OneSplitSmartTokenView, - //OneSplitUniswapV2PoolTokenView + OneSplitUniswapV2PoolTokenView { IOneSplitView public oneSplitView; @@ -95,20 +95,20 @@ contract OneSplitViewWrap is contract OneSplitWrap is OneSplitBaseWrap, - OneSplitMultiPath, - //OneSplitChai, - //OneSplitBdai, - //OneSplitAave, - //OneSplitFulcrum, + //OneSplitMultiPath, + OneSplitChai, + OneSplitBdai, + OneSplitAave, +// OneSplitFulcrum, OneSplitCompound, OneSplitIearn, - //OneSplitIdle, + OneSplitIdle, OneSplitWeth, //OneSplitBalancerPoolToken, //OneSplitUniswapPoolToken, - OneSplitCurvePoolToken + //OneSplitCurvePoolToken //OneSplitSmartToken, - //OneSplitUniswapV2PoolToken + OneSplitUniswapV2PoolToken { IOneSplitView public oneSplitView; IOneSplit public oneSplit; diff --git a/contracts/OneSplitBalancerPoolToken.sol b/contracts/OneSplitBalancerPoolToken.sol index 88f2a25..a5c4ca4 100644 --- a/contracts/OneSplitBalancerPoolToken.sol +++ b/contracts/OneSplitBalancerPoolToken.sol @@ -42,6 +42,7 @@ contract OneSplitBalancerPoolTokenBase { } + contract OneSplitBalancerPoolTokenView is OneSplitViewWrapBase, OneSplitBalancerPoolTokenBase { function getExpectedReturn( diff --git a/contracts/OneSplitCurvePoolToken.sol b/contracts/OneSplitCurvePoolToken.sol index 55f746c..cd925e9 100644 --- a/contracts/OneSplitCurvePoolToken.sol +++ b/contracts/OneSplitCurvePoolToken.sol @@ -320,7 +320,7 @@ contract OneSplitCurvePoolTokenView is OneSplitViewWrapBase, OneSplitCurvePoolTo ) ); - require(success, 'calc_token_amount failed'); + require(success, "calc_token_amount failed"); return (abi.decode(data, (uint256)), distribution); } @@ -392,7 +392,7 @@ contract OneSplitCurvePoolToken is OneSplitBaseWrap, OneSplitCurvePoolTokenBase ) ); - require(success, 'remove_liquidity failed'); + require(success, "remove_liquidity failed"); uint256[] memory dist = new uint256[](distribution.length); for (uint i = 0; i < curveInfo.tokenCount; i++) { @@ -474,6 +474,6 @@ contract OneSplitCurvePoolToken is OneSplitBaseWrap, OneSplitCurvePoolTokenBase ) ); - require(success, 'add_liquidity failed'); + require(success, "add_liquidity failed"); } } diff --git a/contracts/OneSplitSmartToken.sol b/contracts/OneSplitSmartToken.sol index 0cea220..50f2f0f 100644 --- a/contracts/OneSplitSmartToken.sol +++ b/contracts/OneSplitSmartToken.sol @@ -6,6 +6,7 @@ import "./interface/ISmartTokenConverter.sol"; import "./interface/ISmartTokenFormula.sol"; import "./OneSplitBase.sol"; + contract OneSplitSmartTokenBase { using SafeMath for uint256; diff --git a/contracts/OneSplitUniswapPoolToken.sol b/contracts/OneSplitUniswapPoolToken.sol index 48a99eb..c160491 100644 --- a/contracts/OneSplitUniswapPoolToken.sol +++ b/contracts/OneSplitUniswapPoolToken.sol @@ -50,6 +50,7 @@ contract OneSplitUniswapPoolTokenBase { } + contract OneSplitUniswapPoolTokenView is OneSplitViewWrapBase, OneSplitUniswapPoolTokenBase { function getExpectedReturn( diff --git a/contracts/OneSplitUniswapV2PoolToken.sol b/contracts/OneSplitUniswapV2PoolToken.sol index abde290..8e3252c 100644 --- a/contracts/OneSplitUniswapV2PoolToken.sol +++ b/contracts/OneSplitUniswapV2PoolToken.sol @@ -1,14 +1,35 @@ pragma solidity ^0.5.0; import "./OneSplitBase.sol"; -import "./interface/IUniswapV2Pool.sol"; +import "./interface/IUniswapV2Router.sol"; import "./interface/IUniswapV2Pair.sol"; +library Math { + function min(uint x, uint y) internal pure returns (uint z) { + z = x < y ? x : y; + } + + // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) + function sqrt(uint y) internal pure returns (uint z) { + if (y > 3) { + z = y; + uint x = y / 2 + 1; + while (x < z) { + z = x; + x = (y / x + x) / 2; + } + } else if (y != 0) { + z = 1; + } + } +} + + contract OneSplitUniswapV2PoolTokenBase { using SafeMath for uint256; - IUniswapV2Pool constant uniswapPool = IUniswapV2Pool(0x3f6CDd93e4A1c2Df9934Cb90D09040CcFc155F93); + IUniswapV2Router constant uniswapRouter = IUniswapV2Router(0xf164fC0Ec4E93095b804a4795bBe1e041497b92a); function isLiquidityPool(IERC20 token) internal view returns (bool) { (bool success, bytes memory data) = address(token).staticcall.gas(2000)( @@ -36,17 +57,31 @@ contract OneSplitUniswapV2PoolTokenBase { details.tokens[0] = TokenInfo({ token: pair.token0(), reserve: reserve0 - }); + }); details.tokens[1] = TokenInfo({ token: pair.token1(), reserve: reserve1 - }); + }); details.totalSupply = IERC20(address(pair)).totalSupply(); } + function _calcRebalanceAmount( + uint256 leftover, + uint256 balanceOfLeftoverAsset, + uint256 secondAssetBalance + ) internal pure returns (uint256) { + + return Math.sqrt( + 3988000 * leftover * balanceOfLeftoverAsset + + 3988009 * balanceOfLeftoverAsset * balanceOfLeftoverAsset - + 9 * balanceOfLeftoverAsset * balanceOfLeftoverAsset / (secondAssetBalance - 1) + ) / 1994 - balanceOfLeftoverAsset * 1997 / 1994; + } + } + contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswapV2PoolTokenBase { function getExpectedReturn( @@ -149,6 +184,7 @@ contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswap distribution = new uint256[](DEXES_COUNT); PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); + for (uint i = 0; i < 2; i++) { uint256 exchangeAmount = amount @@ -195,18 +231,18 @@ contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswap PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); + // will overwritten to liquidity amounts uint256[2] memory amounts; amounts[0] = amount.div(2); amounts[1] = amount.sub(amounts[0]); + uint256[] memory dist = new uint256[](distribution.length); for (uint i = 0; i < 2; i++) { if (fromToken == details.tokens[i].token) { - uint256 liquidity = amounts[i].mul(details.totalSupply).div(details.tokens[i].reserve); - returnAmount = liquidity > returnAmount ? liquidity : returnAmount; continue; } - (uint256 ret, uint256[] memory dist) = this.getExpectedReturn( + (amounts[i], dist) = this.getExpectedReturn( fromToken, details.tokens[i].token, amounts[i], @@ -214,14 +250,58 @@ contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswap flags ); - uint256 liquidity = ret.mul(details.totalSupply).div(details.tokens[i].reserve); - returnAmount = liquidity > returnAmount ? liquidity : returnAmount; - for (uint j = 0; j < distribution.length; j++) { distribution[j] |= dist[j] << (i * 8); } } + uint256 possibleLiquidity0 = amounts[0].mul(details.totalSupply).div(details.tokens[0].reserve); + returnAmount = Math.min( + possibleLiquidity0, + amounts[1].mul(details.totalSupply).div(details.tokens[1].reserve) + ); + + uint256 leftoverIndex = possibleLiquidity0 > returnAmount ? 0 : 1; + IERC20[] memory path = new IERC20[](2); + path[0] = details.tokens[leftoverIndex].token; + path[1] = details.tokens[1 - leftoverIndex].token; + + uint256 optimalAmount = amounts[1 - leftoverIndex].mul( + details.tokens[leftoverIndex].reserve + ).div(details.tokens[1 - leftoverIndex].reserve); + + IERC20 _poolToken = poolToken; // stack too deep + uint256 exchangeAmount = _calcRebalanceAmount( + amounts[leftoverIndex].sub(optimalAmount), + path[0].balanceOf(address(_poolToken)).add(optimalAmount), + path[1].balanceOf(address(_poolToken)).add(amounts[1 - leftoverIndex]) + ); + + (bool success, bytes memory data) = address(uniswapRouter).staticcall.gas(500000)( + abi.encodeWithSelector( + uniswapRouter.getAmountsOut.selector, + exchangeAmount, + path + ) + ); + + if (!success) { + return ( + returnAmount, + distribution + ); + } + + uint256[] memory amountsOutAfterSwap = abi.decode(data, (uint256[])); + + uint256 _addedLiquidity = returnAmount; // stack too deep + PoolDetails memory _details = details; // stack too deep + returnAmount = _addedLiquidity.add( + amountsOutAfterSwap[1] // amountOut after swap + .mul(_details.totalSupply.add(_addedLiquidity)) + .div(_details.tokens[leftoverIndex].reserve.sub(amountsOutAfterSwap[1])) + ); + return ( returnAmount, distribution @@ -315,23 +395,27 @@ contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTo uint256[] memory distribution, uint256 flags ) private { - _infiniteApproveIfNeeded(poolToken, address(uniswapPool)); - - uint256[2] memory amounts = uniswapPool.removeLiquidity( - IUniswapV2Pair(address(poolToken)), - amount, - [ - uint256(0), - uint256(0) - ] + _infiniteApproveIfNeeded(poolToken, address(uniswapRouter)); + + IERC20 [2] memory tokens = [ + IUniswapV2Pair(address(poolToken)).token0(), + IUniswapV2Pair(address(poolToken)).token1() + ]; + + uint256[2] memory amounts = uniswapRouter.removeLiquidity( + tokens[0], + tokens[1], + amount, + uint256(0), + uint256(0), + address(this), + now.add(1800) ); uint256[] memory dist = new uint256[](distribution.length); - - PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); for (uint i = 0; i < 2; i++) { - if (toToken == details.tokens[i].token) { + if (toToken == tokens[i]) { continue; } @@ -340,7 +424,7 @@ contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTo } this.swap( - details.tokens[i].token, + tokens[i], toToken, amounts[i], 0, @@ -357,7 +441,10 @@ contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTo uint256[] memory distribution, uint256 flags ) private { - PoolDetails memory details = _getPoolDetails(IUniswapV2Pair(address(poolToken))); + IERC20 [2] memory tokens = [ + IUniswapV2Pair(address(poolToken)).token0(), + IUniswapV2Pair(address(poolToken)).token1() + ]; // will overwritten to liquidity amounts uint256[2] memory amounts; @@ -366,9 +453,9 @@ contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTo uint256[] memory dist = new uint256[](distribution.length); for (uint i = 0; i < 2; i++) { - _infiniteApproveIfNeeded(details.tokens[i].token, address(uniswapPool)); + _infiniteApproveIfNeeded(tokens[i], address(uniswapRouter)); - if (fromToken == details.tokens[i].token) { + if (fromToken == tokens[i]) { continue; } @@ -378,23 +465,74 @@ contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTo this.swap( fromToken, - details.tokens[i].token, + tokens[i], amounts[i], 0, dist, flags ); - amounts[i] = details.tokens[i].token.universalBalanceOf(address(this)); + amounts[i] = tokens[i].universalBalanceOf(address(this)); + } + + (uint256[2] memory redeemAmounts, ) = uniswapRouter.addLiquidity( + tokens[0], + tokens[1], + amounts[0], + amounts[1], + uint256(0), + uint256(0), + address(this), + now.add(1800) + ); + + if ( + redeemAmounts[0] == amounts[0] && + redeemAmounts[1] == amounts[1] + ) { + return; } - uniswapPool.addLiquidity(IUniswapV2Pair(address(poolToken)), amounts, 0); + uint256 leftoverIndex = amounts[0] != redeemAmounts[0] ? 0 : 1; + IERC20[] memory path = new IERC20[](2); + path[0] = tokens[leftoverIndex]; + path[1] = tokens[1 - leftoverIndex]; - for (uint i = 0; i < 2; i++) { - details.tokens[i].token.universalTransfer( - msg.sender, - details.tokens[i].token.universalBalanceOf(address(this)) - ); + uint256 exchangeAmount = _calcRebalanceAmount( + amounts[leftoverIndex].sub(redeemAmounts[leftoverIndex]), + path[0].balanceOf(address(poolToken)), + path[1].balanceOf(address(poolToken)) + ); + + (bool success, bytes memory data) = address(uniswapRouter).call.gas(1000000)( + abi.encodeWithSelector( + uniswapRouter.swapExactTokensForTokens.selector, + exchangeAmount, + uint256(0), + path, + address(this), + now.add(1800) + ) + ); + + if (!success) { + return; } + + uint256[] memory amountsOut = abi.decode(data, (uint256[])); + + address(uniswapRouter).call.gas(1000000)( + abi.encodeWithSelector( + uniswapRouter.addLiquidity.selector, + tokens[0], + tokens[1], + amountsOut[leftoverIndex], + amountsOut[1 - leftoverIndex], + uint256(0), + uint256(0), + address(this), + now.add(1800) + ) + ); } } diff --git a/contracts/interface/IBPool.sol b/contracts/interface/IBPool.sol index 42d4dd5..b9a75d2 100644 --- a/contracts/interface/IBPool.sol +++ b/contracts/interface/IBPool.sol @@ -2,10 +2,12 @@ pragma solidity ^0.5.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + contract BConst { uint public constant EXIT_FEE = 0; } + contract IBMath is BConst { function calcPoolOutGivenSingleIn( uint tokenBalanceIn, @@ -19,6 +21,7 @@ contract IBMath is BConst { pure returns (uint poolAmountOut); } + contract IBPool is IERC20, IBMath { function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn) external; diff --git a/contracts/interface/IUniswapV2Pool.sol b/contracts/interface/IUniswapV2Pool.sol deleted file mode 100644 index 2767ee0..0000000 --- a/contracts/interface/IUniswapV2Pool.sol +++ /dev/null @@ -1,21 +0,0 @@ -pragma solidity ^0.5.0; - -import "./IUniswapV2Pair.sol"; - -interface IUniswapV2Pool { - function addLiquidity( - IUniswapV2Pair pool, - uint256[2] calldata amounts, - uint256 minMintAmount - ) - external - returns (uint256); - - function removeLiquidity( - IUniswapV2Pair pool, - uint256 burnAmount, - uint256[2] calldata minReturnAmount - ) - external - returns (uint256[2] memory); -} diff --git a/contracts/interface/IUniswapV2Router.sol b/contracts/interface/IUniswapV2Router.sol new file mode 100644 index 0000000..d74e192 --- /dev/null +++ b/contracts/interface/IUniswapV2Router.sol @@ -0,0 +1,36 @@ +pragma solidity ^0.5.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface IUniswapV2Router { + function addLiquidity( + IERC20 tokenA, + IERC20 tokenB, + uint amountADesired, + uint amountBDesired, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) external returns (uint256[2] memory amounts, uint liquidity); + + function removeLiquidity( + IERC20 tokenA, + IERC20 tokenB, + uint liquidity, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) external returns (uint256[2] memory); + + function swapExactTokensForTokens( + uint amountIn, + uint amountOutMin, + IERC20[] calldata path, + address to, + uint deadline + ) external returns (uint[] memory amounts); + + function getAmountsOut(uint amountIn, IERC20[] calldata path) external view returns (uint[] memory amounts); +} diff --git a/test/OneSplit.js b/test/OneSplit.js index af95038..0694638 100644 --- a/test/OneSplit.js +++ b/test/OneSplit.js @@ -8,7 +8,6 @@ const OneSplit = artifacts.require('OneSplit'); const OneSplitWrap = artifacts.require('OneSplitWrap'); contract('OneSplit', function ([_, addr1]) { - describe('OneSplit', async function () { beforeEach('should be ok', async function () { const subSplitView = await OneSplitView.new(); From b8457017b917265106c7652f8a335b382f702e04 Mon Sep 17 00:00:00 2001 From: Kirill Date: Wed, 20 May 2020 04:12:10 +0300 Subject: [PATCH 60/60] last --- OneSplit.full.bin | 2 +- OneSplit.full.sol | 27 ++++++++++++++++-------- contracts/OneSplitUniswapV2PoolToken.sol | 20 ++++++++++++------ 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/OneSplit.full.bin b/OneSplit.full.bin index bf776e4..b21ac04 100644 --- a/OneSplit.full.bin +++ b/OneSplit.full.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b5060405162004a7938038062004a798339818101604052602081101561003557600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055614a1180620000686000396000f3fe60806040526004361061041b5760003560e01c8063819faf7b1161021e578063c989b66711610123578063dc1536b2116100ab578063f4b9fa751161007a578063f4b9fa7514610ae2578063f56e281f14610af7578063f69e204614610b0c578063fa3f110b14610b21578063fbe4ed9514610b365761041b565b8063dc1536b2146109d9578063e2a7515e146109ee578063e355812314610ab8578063e44987b414610acd5761041b565b8063cede5f6a116100f2578063cede5f6a14610985578063d1aee5e314610580578063d393c3e91461099a578063d70a2d1f146109af578063d77366a4146109c45761041b565b8063c989b66714610931578063c9b42c6714610946578063cc26e9fc1461095b578063ce74b7ac146109705761041b565b8063b0a7ef29116101a6578063bf2c5a0711610175578063bf2c5a07146108c8578063c762a46c146108dd578063c77b9de6146108f2578063c7f112e414610907578063c92577751461091c5761041b565b8063b0a7ef2914610889578063b184a3ae1461089e578063b3bc7844146107f6578063b69d0456146108b35761041b565b80638ea812c0116101ed5780638ea812c014610820578063a1b4d01114610835578063a2878cb11461084a578063a4792ab31461085f578063a734f06e146108745761041b565b8063819faf7b146107cc578063851954fa146107e15780638aea49d2146107f65780638bdb2afa1461080b5761041b565b80634226a9b9116103245780635ae51b82116102ac5780636cbc4a6e1161027b5780636cbc4a6e1461076357806375a8b0121461077857806375b5be2d1461078d5780637a88bdbd146107a25780637e09b9c2146107b75761041b565b80635ae51b821461070f5780635c0cb4791461072457806364ec4e5c1461073957806368e2a0141461074e5761041b565b80634a7101d5116102f35780634a7101d5146106a65780635187c091146106bb57806351f1985c146106d057806352a701b4146106e55780635aa8fb48146106fa5761041b565b80634226a9b9146105aa578063423d03f91461066757806344211d621461067c5780634752c680146106915761041b565b80632d3b5207116103a7578063372a26cb11610376578063372a26cb146105fe5780633ca5b234146106135780633e413bee146106285780633fc8cef31461063d57806340ab7b8c146106525761041b565b80632d3b5207146105aa5780632e707bd2146105bf5780632f48ab7d146105d457806334b4dabb146105e95761041b565b806313989140116103ee57806313989140146105415780631d209b65146105565780632113240d1461056b57806321a360f51461058057806322320c98146105955761041b565b806305d8aa0a1461042a578063085e2c5b1461045157806312dea160146104fb5780631388b4201461052c575b3332141561042857600080fd5b005b34801561043657600080fd5b5061043f610b4b565b60408051918252519081900360200190f35b34801561045d57600080fd5b506104a0600480360360a081101561047457600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060800135610b52565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b838110156104e65781810151838201526020016104ce565b50505050905001935050505060405180910390f35b34801561050757600080fd5b50610510610c9e565b604080516001600160a01b039092168252519081900360200190f35b34801561053857600080fd5b50610510610cb6565b34801561054d57600080fd5b5061043f610cce565b34801561056257600080fd5b5061043f610cd4565b34801561057757600080fd5b5061043f610cdc565b34801561058c57600080fd5b5061043f610ce2565b3480156105a157600080fd5b50610510610ceb565b3480156105b657600080fd5b5061043f610d03565b3480156105cb57600080fd5b5061043f610d0c565b3480156105e057600080fd5b50610510610d11565b3480156105f557600080fd5b5061043f610d23565b34801561060a57600080fd5b50610510610d28565b34801561061f57600080fd5b50610510610d40565b34801561063457600080fd5b50610510610d58565b34801561064957600080fd5b50610510610d6a565b34801561065e57600080fd5b50610510610d82565b34801561067357600080fd5b50610510610d9a565b34801561068857600080fd5b5061043f610db2565b34801561069d57600080fd5b5061043f610db7565b3480156106b257600080fd5b5061043f610dbf565b3480156106c757600080fd5b50610510610dc4565b3480156106dc57600080fd5b50610510610ddc565b3480156106f157600080fd5b50610510610df4565b34801561070657600080fd5b5061043f610e0c565b34801561071b57600080fd5b5061043f610e12565b34801561073057600080fd5b5061043f610e18565b34801561074557600080fd5b5061043f610e1d565b34801561075a57600080fd5b5061043f610e24565b34801561076f57600080fd5b5061043f610e2b565b34801561078457600080fd5b5061043f610e32565b34801561079957600080fd5b50610510610e38565b3480156107ae57600080fd5b5061043f610e4b565b3480156107c357600080fd5b5061043f610e50565b3480156107d857600080fd5b50610510610e57565b3480156107ed57600080fd5b50610510610e6f565b34801561080257600080fd5b5061043f610e87565b34801561081757600080fd5b50610510610e90565b34801561082c57600080fd5b5061043f610ea8565b34801561084157600080fd5b50610510610eb1565b34801561085657600080fd5b5061043f610ec9565b34801561086b57600080fd5b50610510610ed1565b34801561088057600080fd5b50610510610ee9565b34801561089557600080fd5b5061043f610f01565b3480156108aa57600080fd5b50610510610f07565b3480156108bf57600080fd5b50610510610f1f565b3480156108d457600080fd5b5061043f610f37565b3480156108e957600080fd5b5061043f610f3f565b3480156108fe57600080fd5b5061043f610f44565b34801561091357600080fd5b5061043f610f4a565b34801561092857600080fd5b50610510610f52565b34801561093d57600080fd5b5061043f610f6a565b34801561095257600080fd5b5061043f610f71565b34801561096757600080fd5b5061043f610f78565b34801561097c57600080fd5b5061043f610f7d565b34801561099157600080fd5b50610510610f85565b3480156109a657600080fd5b5061043f610f9d565b3480156109bb57600080fd5b50610510610fa4565b3480156109d057600080fd5b50610510610fbc565b3480156109e557600080fd5b5061043f610fd4565b610428600480360360c0811015610a0457600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135640100000000811115610a4457600080fd5b820183602082011115610a5657600080fd5b80359060200191846020830284011164010000000083111715610a7857600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610fda915050565b348015610ac457600080fd5b5061043f61122c565b348015610ad957600080fd5b5061043f611234565b348015610aee57600080fd5b5061051061123c565b348015610b0357600080fd5b5061043f61124e565b348015610b1857600080fd5b50610510611253565b348015610b2d57600080fd5b5061043f61126b565b348015610b4257600080fd5b50610510611273565b6220000081565b600080546040805163085e2c5b60e01b81526001600160a01b03898116600483015288811660248301526044820188905260648201879052608482018690529151606093929092169163085e2c5b9160a4808201928792909190829003018186803b158015610bc057600080fd5b505afa158015610bd4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610bfd57600080fd5b815160208301805160405192949293830192919084640100000000821115610c2457600080fd5b908301906020820185811115610c3957600080fd5b8251866020820283011164010000000082111715610c5657600080fd5b82525081516020918201928201910280838360005b83811015610c83578181015183820152602001610c6b565b50505050905001604052505050915091509550959350505050565b7352ae12abe5d8bd778bd5397f99ca900624cfadd481565b73794e6e91555438afc3ccf1c5076a74f42133d08d81565b61200081565b630400000081565b61800081565b64020000000081565b73a5407eae9ba41422680e2e00537571bcc53efbfd81565b64010000000081565b608081565b60008051602061491b83398151915281565b604081565b7379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2781565b734fabb145d64652a948d72533023f6e7a623c7c5381565b6000805160206148fb83398151915281565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c81565b7345f783cce6b7ff23b2ab2d70e416cdb7d6055f5181565b601081565b631e00000081565b602081565b735c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f81565b73a2b47e3d5c44877cca798226b7b8118f9bfb7a5681565b738e870d67f660d95d5be530380d0ec0bd388289e181565b61400081565b61080081565b600881565b6202000081565b6210000081565b6208000081565b61040081565b6e085d4780b73119b644ae5ecd22b37681565b600281565b6240000081565b73398ec7346dcd622edc5ae82352f02be94c62d11981565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce31581565b64040000000081565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9581565b64080000000081565b734ddc2d193948926d02f9b1fe9e1daa0718270ed581565b634000000081565b737079e8517594e5b21d2b9a0d17cb33f5fe2bca7081565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b7306364f10b501e868329afbc005b3492902d6c76381565b7306af07097c9eeb7fd685c692751d5c66db49c21581565b630800000081565b600181565b61020081565b638000000081565b7357ab1ec28d129707052df4df418d58a2d46d5f5181565b6280000081565b6204000081565b601281565b630200000081565b7352ea46506b9cc5ef470c5bf89f17dc28bb35d85c81565b6201000081565b73f6e2d7f616b67e46d708e4410746e9aab3a4c51881565b73818e6fecd516ecc3849daf6845e3ec868087b75581565b61010081565b846001600160a01b0316866001600160a01b03161415610ff957611224565b61100161485f565b6040518061024001604052806112828152602001611503815260200161168e81526020016119b38152602001611c8c8152602001611e178152602001611fdc81526020016121f58152602001612418815260200161263b81526020016127d981526020016129858152602001612af18152602001612c398152602001612c468152602001612c688152602001612c848152602001612ca081525090506012835111156110de5760405162461bcd60e51b815260040180806020018281038252604281526020018061499b6042913960600191505060405180910390fd5b600080805b855181101561113c5760008682815181106110fa57fe5b602002602001015111156111345761112e86828151811061111757fe5b602002602001015184612ec390919063ffffffff16565b92508091505b6001016110e3565b506000821161117c5760405162461bcd60e51b815260040180806020018281038252602f8152602001806148ab602f913960400191505060405180910390fd5b8660005b865181101561121e5786818151811061119557fe5b6020026020010151600014156111aa57611216565b60006111e2856111d68a85815181106111bf57fe5b60200260200101518d612f2690919063ffffffff16565b9063ffffffff612f7f16565b9050838214156111ef5750815b80830392506112138c8c8389866012811061120657fe5b602002015163ffffffff16565b50505b600101611180565b50505050505b505050505050565b631000000081565b632000000081565b60008051602061488b83398151915281565b600481565b733d9819210a31b4961b30ef54be2aed79b9c9cd3b81565b630100000081565b6000546001600160a01b031681565b6000816112976001600160a01b038616612fc1565b6113c757604080516303795fb160e11b81526001600160a01b0387166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156112f957600080fd5b505afa15801561130d573d6000803e3d6000fd5b505050506040513d602081101561132357600080fd5b505190506001600160a01b038116156113c5576113408682612ffd565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561139657600080fd5b505af11580156113aa573d6000803e3d6000fd5b505050506040513d60208110156113c057600080fd5b505191505b505b6113d9846001600160a01b0316612fc1565b6114f957604080516303795fb160e11b81526001600160a01b0386166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b15801561143b57600080fd5b505afa15801561144f573d6000803e3d6000fd5b505050506040513d602081101561146557600080fd5b505190506001600160a01b038116156114f757806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b1580156114c757600080fd5b505af11580156114db573d6000803e3d6000fd5b50505050506040513d60208110156114f257600080fd5b505191505b505b90505b9392505050565b60006115238473818e6fecd516ecc3849daf6845e3ec868087b755612ffd565b73818e6fecd516ecc3849daf6845e3ec868087b7556329589f6161154f6001600160a01b038716612fc1565b61155a57600061155c565b835b61156e876001600160a01b0316612fc1565b611578578661158e565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b856115a1886001600160a01b0316612fc1565b6115ab57876115c1565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b15801561165957600080fd5b505af115801561166d573d6000803e3d6000fd5b50505050506040513d602081101561168457600080fd5b5051949350505050565b60006116a2846001600160a01b0316612fc1565b156117105773c0829421c1d260bd3cb3e0f06cfe2d52db2ce3156001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156116f657600080fd5b505af115801561170a573d6000803e3d6000fd5b50505050505b60007352ae12abe5d8bd778bd5397f99ca900624cfadd46001600160a01b031663bb34534c6040518163ffffffff1660e01b815260040180806c42616e636f724e6574776f726b60981b815250602001905060206040518083038186803b15801561177a57600080fd5b505afa15801561178e573d6000803e3d6000fd5b505050506040513d60208110156117a457600080fd5b5051905060606117b486866130b6565b90506117f16117cb876001600160a01b0316612fc1565b6117d557866117eb565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b83612ffd565b6000826001600160a01b031663c7ba24bc838760016040518463ffffffff1660e01b81526004018080602001848152602001838152602001828103825285818151815260200191508051906020019060200280838360005b83811015611861578181015183820152602001611849565b50505050905001945050505050602060405180830381600087803b15801561188857600080fd5b505af115801561189c573d6000803e3d6000fd5b505050506040513d60208110156118b257600080fd5b505190506118c86001600160a01b038716612fc1565b156119a957604080516370a0823160e01b8152306004820152905173c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591632e1a7d4d9183916370a08231916024808301926020929190829003018186803b15801561192657600080fd5b505afa15801561193a573d6000803e3d6000fd5b505050506040513d602081101561195057600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561199057600080fd5b505af11580156119a4573d6000803e3d6000fd5b505050505b9695505050505050565b60006119c7846001600160a01b0316612fc1565b15611a355773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015611a1b57600080fd5b505af1158015611a2f573d6000803e3d6000fd5b50505050505b611a84611a4a856001600160a01b0316612fc1565b611a545784611a6a565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b73794e6e91555438afc3ccf1c5076a74f42133d08d612ffd565b600073794e6e91555438afc3ccf1c5076a74f42133d08d630621b4f6611ab26001600160a01b038816612fc1565b611abc5786611ad2565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b85611ae5886001600160a01b0316612fc1565b611aef5787611b05565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b158015611b6357600080fd5b505af1158015611b77573d6000803e3d6000fd5b505050506040513d6020811015611b8d57600080fd5b50519050611ba36001600160a01b038516612fc1565b156114f957604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b158015611c0157600080fd5b505afa158015611c15573d6000803e3d6000fd5b505050506040513d6020811015611c2b57600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015611c6b57600080fd5b505af1158015611c7f573d6000803e3d6000fd5b5050505090509392505050565b6000806001600160a01b0385166000805160206148fb83398151915214611cb4576000611cb7565b60025b6001600160a01b03861660008051602061488b83398151915214611cdc576000611cdf565b60015b0160ff16905060006000805160206148fb8339815191526001600160a01b03861614611d0c576000611d0f565b60025b6001600160a01b03861660008051602061488b83398151915214611d34576000611d37565b60015b0160ff16905081600f0b60001480611d52575080600f0b6000145b15611d62576000925050506114fc565b611d808673a2b47e3d5c44877cca798226b7b8118f9bfb7a56612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a2b47e3d5c44877cca798226b7b8118f9bfb7a569263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b505af1158015611e0a573d6000803e3d6000fd5b5050505050509392505050565b6000806001600160a01b03851660008051602061491b83398151915214611e3f576000611e42565b60035b6001600160a01b0386166000805160206148fb83398151915214611e67576000611e6a565b60025b6001600160a01b03871660008051602061488b83398151915214611e8f576000611e92565b60015b010160ff169050600060008051602061491b8339815191526001600160a01b0316856001600160a01b031614611ec9576000611ecc565b60035b6001600160a01b0386166000805160206148fb83398151915214611ef1576000611ef4565b60025b6001600160a01b03871660008051602061488b83398151915214611f19576000611f1c565b60015b010160ff16905081600f0b60001480611f38575080600f0b6000145b15611f48576000925050506114fc565b611f66867352ea46506b9cc5ef470c5bf89f17dc28bb35d85c612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517352ea46506b9cc5ef470c5bf89f17dc28bb35d85c9263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b6000806001600160a01b0385166e085d4780b73119b644ae5ecd22b37614612005576000612008565b60045b6001600160a01b03861660008051602061491b8339815191521461202d576000612030565b60035b6001600160a01b0387166000805160206148fb83398151915214612055576000612058565b60025b6001600160a01b03881660008051602061488b8339815191521461207d576000612080565b60015b01010160ff16905060006e085d4780b73119b644ae5ecd22b3766001600160a01b0316856001600160a01b0316146120b95760006120bc565b60045b6001600160a01b03861660008051602061491b833981519152146120e15760006120e4565b60035b6001600160a01b0387166000805160206148fb8339815191521461210957600061210c565b60025b6001600160a01b03881660008051602061488b83398151915214612131576000612134565b60015b01010160ff16905081600f0b60001480612151575080600f0b6000145b15612161576000925050506114fc565b61217f867345f783cce6b7ff23b2ab2d70e416cdb7d6055f51612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517345f783cce6b7ff23b2ab2d70e416cdb7d6055f519263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b6000806001600160a01b038516734fabb145d64652a948d72533023f6e7a623c7c5314612223576000612226565b60045b6001600160a01b03861660008051602061491b8339815191521461224b57600061224e565b60035b6001600160a01b0387166000805160206148fb83398151915214612273576000612276565b60025b6001600160a01b03881660008051602061488b8339815191521461229b57600061229e565b60015b01010160ff1690506000734fabb145d64652a948d72533023f6e7a623c7c536001600160a01b0316856001600160a01b0316146122dc5760006122df565b60045b6001600160a01b03861660008051602061491b83398151915214612304576000612307565b60035b6001600160a01b0387166000805160206148fb8339815191521461232c57600061232f565b60025b6001600160a01b03881660008051602061488b83398151915214612354576000612357565b60015b01010160ff16905081600f0b60001480612374575080600f0b6000145b15612384576000925050506114fc565b6123a2867379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517379a8c46dea5ada233abaffd40f3a0a2b1e5a4f279263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b6000806001600160a01b0385167357ab1ec28d129707052df4df418d58a2d46d5f5114612446576000612449565b60045b6001600160a01b03861660008051602061491b8339815191521461246e576000612471565b60035b6001600160a01b0387166000805160206148fb83398151915214612496576000612499565b60025b6001600160a01b03881660008051602061488b833981519152146124be5760006124c1565b60015b01010160ff16905060007357ab1ec28d129707052df4df418d58a2d46d5f516001600160a01b0316856001600160a01b0316146124ff576000612502565b60045b6001600160a01b03861660008051602061491b8339815191521461252757600061252a565b60035b6001600160a01b0387166000805160206148fb8339815191521461254f576000612552565b60025b6001600160a01b03881660008051602061488b8339815191521461257757600061257a565b60015b01010160ff16905081600f0b60001480612597575080600f0b6000145b156125a7576000925050506114fc565b6125c58673a5407eae9ba41422680e2e00537571bcc53efbfd612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a5407eae9ba41422680e2e00537571bcc53efbfd9263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b600061264f846001600160a01b0316612fc1565b61270857600061265e856137c0565b905061266a8582612ffd565b806001600160a01b031663a0712d68846040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156126b057600080fd5b505af11580156126c4573d6000803e3d6000fd5b505050506040513d60208110156126da57600080fd5b50612700905081856126fb6001600160a01b0383163063ffffffff6139aa16565b611282565b9150506114fc565b61271a836001600160a01b0316612fc1565b6127cf576000612729846137c0565b90506000612738868386611282565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561278057600080fd5b505af1158015612794573d6000803e3d6000fd5b505050506040513d60208110156127aa57600080fd5b506127c690506001600160a01b0386163063ffffffff6139aa16565b925050506114fc565b5060009392505050565b60006001600160a01b03841660008051602061488b83398151915214156128bc57612818847306af07097c9eeb7fd685c692751d5c66db49c215612ffd565b60408051633b4da69f60e01b81523060048201526024810184905290517306af07097c9eeb7fd685c692751d5c66db49c21591633b4da69f91604480830192600092919082900301818387803b15801561287157600080fd5b505af1158015612885573d6000803e3d6000fd5b506128b592507306af07097c9eeb7fd685c692751d5c66db49c21591508590506126fb823063ffffffff6139aa16565b90506114fc565b6001600160a01b03831660008051602061488b83398151915214156127cf5760006128fc857306af07097c9eeb7fd685c692751d5c66db49c21585611282565b6040805163ef693bed60e01b81523060048201526024810183905290519192507306af07097c9eeb7fd685c692751d5c66db49c2159163ef693bed9160448082019260009290919082900301818387803b15801561295957600080fd5b505af115801561296d573d6000803e3d6000fd5b50612700925050506001600160a01b038516306139aa565b6000612999846001600160a01b0316612fc1565b612a575760006129a885613a54565b90506129b48582612ffd565b60408051636968703360e11b81526001600160a01b03871660048201526024810185905261044d6044820152905173398ec7346dcd622edc5ae82352f02be94c62d1199163d2d0e06691606480830192600092919082900301818387803b158015612a1e57600080fd5b505af1158015612a32573d6000803e3d6000fd5b5050505061270081856126fb30856001600160a01b03166139aa90919063ffffffff16565b612a69836001600160a01b0316612fc1565b6127cf576000612a7884613a54565b90506000612a87868386611282565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015612acf57600080fd5b505af1158015612ae3573d6000803e3d6000fd5b5050505080925050506114fc565b600080737079e8517594e5b21d2b9a0d17cb33f5fe2bca706001600160a01b031663d4b839926040518163ffffffff1660e01b815260040160206040518083038186803b158015612b4157600080fd5b505afa158015612b55573d6000803e3d6000fd5b505050506040513d6020811015612b6b57600080fd5b50519050612b798582612ffd565b806001600160a01b031663fe029156612b9a876001600160a01b0316612fc1565b612ba5576000612ba7565b845b604080516001600160e01b031960e085901b1681526001600160a01b03808b1660048301528916602482015260448101889052600060648201529051608480830192602092919082900301818588803b158015612c0357600080fd5b505af1158015612c17573d6000803e3d6000fd5b50505050506040513d6020811015612c2e57600080fd5b505195945050505050565b60006114f9848484613e71565b60006114f98473c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2858561423a565b60006114f98460008051602061488b833981519152858561423a565b60006114f9846000805160206148fb833981519152858561423a565b6000806001600160a01b038516738e870d67f660d95d5be530380d0ec0bd388289e114612cce576000612cd1565b60045b6001600160a01b03861660008051602061491b83398151915214612cf6576000612cf9565b60035b6001600160a01b0387166000805160206148fb83398151915214612d1e576000612d21565b60025b6001600160a01b03881660008051602061488b83398151915214612d46576000612d49565b60015b01010160ff1690506000738e870d67f660d95d5be530380d0ec0bd388289e16001600160a01b0316856001600160a01b031614612d87576000612d8a565b60045b6001600160a01b03861660008051602061491b83398151915214612daf576000612db2565b60035b6001600160a01b0387166000805160206148fb83398151915214612dd7576000612dda565b60025b6001600160a01b03881660008051602061488b83398151915214612dff576000612e02565b60015b01010160ff16905081600f0b60001480612e1f575080600f0b6000145b15612e2f576000925050506114fc565b612e4d867306364f10b501e868329afbc005b3492902d6c763612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517306364f10b501e868329afbc005b3492902d6c7639263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b600082820183811015612f1d576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b600082612f3557506000612f20565b82820282848281612f4257fe5b0414612f1d5760405162461bcd60e51b81526004018080602001828103825260218152602001806148da6021913960400191505060405180910390fd5b6000612f1d83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061425a565b60006001600160a01b0382161580612ff557506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b90505b919050565b61300f826001600160a01b0316612fc1565b6130b25760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b15801561306457600080fd5b505afa158015613078573d6000803e3d6000fd5b505050506040513d602081101561308e57600080fd5b5051901c6130b2576130b26001600160a01b0383168260001963ffffffff6142fc16565b5050565b6060816001600160a01b0316836001600160a01b031614156130e75750604080516000815260208101909152612f20565b6130f9836001600160a01b0316612fc1565b156131165773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31592505b613128826001600160a01b0316612fc1565b156131455773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591505b6001600160a01b038316731f573d6fb3f13d689ff844b4ce37794d79a7ff1c148061318c57506001600160a01b038216731f573d6fb3f13d689ff844b4ce37794d79a7ff1c145b156131b7576040805160038082526080820190925290602082016060803883390190505090506131d9565b60408051600580825260c08201909252906020820160a0803883390190505090505b6000806001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c146133a2576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6132366001600160a01b038b16612fc1565b6132405789613256565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106132d45780518252601f1990920191602091820191016132b5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613335576040519150601f19603f3d011682016040523d82523d6000602084013e61333a565b606091505b5091509150816133625760408051600080825260208201909252905b50945050505050612f20565b80806020019051602081101561337757600080fd5b505193506001600160a01b03841661339f576040805160008082526020820190925290613356565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14613560576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6133fc6001600160a01b038a16612fc1565b613406578861341c565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b6020831061349a5780518252601f19909201916020918201910161347b565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d80600081146134fb576040519150601f19603f3d011682016040523d82523d6000602084013e613500565b606091505b509150915081613520576040805160008082526020820190925290613356565b80806020019051602081101561353557600080fd5b505192506001600160a01b03831661355d576040805160008082526020820190925290613356565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c141561362357848360008151811061359357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505081836001815181106135c157fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360028151811061360357fe5b6001600160a01b039092166020928302919091019091015250612f209050565b6001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156136c657731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360008151811061366a57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050808360018151811061369857fe5b60200260200101906001600160a01b031690816001600160a01b031681525050838360028151811061360357fe5b84836000815181106136d457fe5b60200260200101906001600160a01b031690816001600160a01b031681525050818360018151811061370257fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360028151811061374457fe5b60200260200101906001600160a01b031690816001600160a01b031681525050808360038151811061377257fe5b60200260200101906001600160a01b031690816001600160a01b03168152505083836004815181106137a057fe5b6001600160a01b0390921660209283029190910190910152505092915050565b60006137d4826001600160a01b0316612fc1565b156137f45750734ddc2d193948926d02f9b1fe9e1daa0718270ed5612ff8565b6001600160a01b03821660008051602061488b833981519152141561382e5750735d3a536e4d6dbd6114cc1ead35777bab948e3643612ff8565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef141561386e5750736c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e612ff8565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e86214156138ae575073158079ee67fce2f58472a96584a73c7ab9ac95c1612ff8565b6001600160a01b0382166000805160206148fb83398151915214156138e857507339aa39c021dfbae8fac545936693ac917d5e7563612ff8565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415613928575073c11b1268c1a384e55c48c2391d8d480264a3a7f4612ff8565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415613968575073b3319f5d18bc0d84dd1b4825dcde5d5f7266d407612ff8565b6001600160a01b03821660008051602061491b83398151915214156139a2575073f650c3d88d12db855b8bf7d11be6c55a4e07dcc9612ff8565b506000919050565b60006139b583612fc1565b156139cb57506001600160a01b03811631612f20565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015613a2157600080fd5b505afa158015613a35573d6000803e3d6000fd5b505050506040513d6020811015613a4b57600080fd5b50519050612f20565b6000613a68826001600160a01b0316612fc1565b15613a885750733a3a65aab0dd2a17e3f1947ba16138cd37d08c04612ff8565b6001600160a01b03821660008051602061488b8339815191521415613ac2575073fc1e690f61efd961294b3e1ce3313fbd8aa4f85d612ff8565b6001600160a01b0382166000805160206148fb8339815191521415613afc5750739ba00d6856a4edf4665bca2c2309936572473b7e612ff8565b6001600160a01b0382167357ab1ec28d129707052df4df418d58a2d46d5f511415613b3c575073625ae63000f46200499120b906716420bd059240612ff8565b6001600160a01b038216734fabb145d64652a948d72533023f6e7a623c7c531415613b7c5750736ee0f7bb50a54ab5253da0667b0dc2ee526c30a8612ff8565b6001600160a01b0382166e085d4780b73119b644ae5ecd22b3761415613bb75750734da9b813057d04baef4e5800e36083717b4a0341612ff8565b6001600160a01b03821660008051602061491b8339815191521415613bf157507371fc860f7d3a592a4a98740e39db31d25db65ae8612ff8565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415613c31575073e1ba0fb44ccb0d11b80f92f4f8ed94ca3ff51d00612ff8565b6001600160a01b03821673dd974d5c2e2928dea5f71b9825b8b646686bd2001415613c715750739d91be44c06d373a8a226e1f3b146956083803eb612ff8565b6001600160a01b0382167380fb784b7ed66730e8b1dbd9820afd29931aab031415613cb15750737d2d3688df45ce7c552e19c27e007673da9204b8612ff8565b6001600160a01b03821673514910771af9ca656af840dff83e8264ecf986ca1415613cf1575073a64bd6c70cb9051f6a9ba1f163fdc07e0dfb5f84612ff8565b6001600160a01b038216730f5d2fb29fb7d3cfee444a200298f468908cc9421415613d315750736fce4a401b6b80ace52baaefe4421bd188e76f6f612ff8565b6001600160a01b038216739f8f72aa9304c8b593d555f12ef6589cc3a579a21415613d715750737deb5e830be29f91e298ba5ff1356bb7f8146998612ff8565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e8621415613db157507371010a9d003445ac60c4e6a7017c1e89a477b438612ff8565b6001600160a01b03821673c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f1415613df1575073328c4c80bc7aca0834db37e6600a6c49e12da4de612ff8565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415613e31575073fc4b8ed459e00e5400be803a9bb3954234fd50e3612ff8565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f49814156139a25750736fb0855c404e09c47c3fbca25f08d4e41f9f062f612ff8565b6000613e85846001600160a01b0316612fc1565b15613ef35773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015613ed957600080fd5b505af1158015613eed573d6000803e3d6000fd5b50505050505b6000613f07856001600160a01b0316612fc1565b613f115784613f27565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b90506000613f3d856001600160a01b0316612fc1565b613f475784613f5d565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b6040805163e6a4390560e01b81526001600160a01b038581166004830152831660248201529051919250600091735c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f9163e6a43905916044808301926020929190829003018186803b158015613fc557600080fd5b505afa158015613fd9573d6000803e3d6000fd5b505050506040513d6020811015613fef57600080fd5b5051905061400e6001600160a01b03821684848863ffffffff6143d216565b935061402a6001600160a01b038416828763ffffffff61448916565b50816001600160a01b0316836001600160a01b031610156140c3576040805163022c0d9f60e01b815260006004820181905260248201879052306044830152608060648301526084820181905291516001600160a01b0384169263022c0d9f9260c4808201939182900301818387803b1580156140a657600080fd5b505af11580156140ba573d6000803e3d6000fd5b5050505061413d565b6040805163022c0d9f60e01b815260048101869052600060248201819052306044830152608060648301526084820181905291516001600160a01b0384169263022c0d9f9260c4808201939182900301818387803b15801561412457600080fd5b505af1158015614138573d6000803e3d6000fd5b505050505b61414f866001600160a01b0316612fc1565b1561423057604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b1580156141ad57600080fd5b505afa1580156141c1573d6000803e3d6000fd5b505050506040513d60208110156141d757600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561421757600080fd5b505af115801561422b573d6000803e3d6000fd5b505050505b5050509392505050565b6000614251848461424c888887613e71565b613e71565b95945050505050565b600081836142e65760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156142ab578181015183820152602001614293565b50505050905090810190601f1680156142d85780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816142f257fe5b0495945050505050565b61430583612fc1565b6143cd57600081118015614393575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561436557600080fd5b505afa158015614379573d6000803e3d6000fd5b505050506040513d602081101561438f57600080fd5b5051115b156143b3576143b36001600160a01b03841683600063ffffffff61450416565b6143cd6001600160a01b038416838363ffffffff61450416565b505050565b6000806143ee6001600160a01b0386168763ffffffff6139aa16565b9050600061440b6001600160a01b0386168863ffffffff6139aa16565b90506000614421856103e563ffffffff612f2616565b90506000614435828463ffffffff612f2616565b9050600061445b8361444f876103e863ffffffff612f2616565b9063ffffffff612ec316565b9050801561447857614473828263ffffffff612f7f16565b61447b565b60005b9a9950505050505050505050565b600081614498575060016114fc565b6144a184612fc1565b156144e2576040516001600160a01b0384169083156108fc029084906000818181858888f193505050501580156144dc573d6000803e3d6000fd5b506114fc565b6144fc6001600160a01b038516848463ffffffff61461716565b5060016114fc565b80158061458a575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b15801561455c57600080fd5b505afa158015614570573d6000803e3d6000fd5b505050506040513d602081101561458657600080fd5b5051155b6145c55760405162461bcd60e51b81526004018080602001828103825260368152602001806149656036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526143cd908490614665565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526143cd9084905b614677826001600160a01b0316614823565b6146c8576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106147065780518252601f1990920191602091820191016146e7565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114614768576040519150601f19603f3d011682016040523d82523d6000602084013e61476d565b606091505b5091509150816147c4576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b80511561481d578080602001905160208110156147e057600080fd5b505161481d5760405162461bcd60e51b815260040180806020018281038252602a81526020018061493b602a913960400191505060405180910390fd5b50505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061485757508115155b949350505050565b6040518061024001604052806012905b61488881526020019060019003908161486f5790505090565bfefe0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f73536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec75361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e63654f6e6553706c69743a20446973747269627574696f6e2061727261792073686f756c64206e6f74206578636565642072657365727665732061727261792073697a65a265627a7a72315820dc9f329eb1e422ec1b4a38de19717cceb09997b1c5b7a3470705e669c2ab2da064736f6c63430005110032 \ No newline at end of file +608060405234801561001057600080fd5b5060405162004a7938038062004a798339818101604052602081101561003557600080fd5b5051600080546001600160a01b039092166001600160a01b0319909216919091179055614a1180620000686000396000f3fe60806040526004361061041b5760003560e01c8063819faf7b1161021e578063c989b66711610123578063dc1536b2116100ab578063f4b9fa751161007a578063f4b9fa7514610ae2578063f56e281f14610af7578063f69e204614610b0c578063fa3f110b14610b21578063fbe4ed9514610b365761041b565b8063dc1536b2146109d9578063e2a7515e146109ee578063e355812314610ab8578063e44987b414610acd5761041b565b8063cede5f6a116100f2578063cede5f6a14610985578063d1aee5e314610580578063d393c3e91461099a578063d70a2d1f146109af578063d77366a4146109c45761041b565b8063c989b66714610931578063c9b42c6714610946578063cc26e9fc1461095b578063ce74b7ac146109705761041b565b8063b0a7ef29116101a6578063bf2c5a0711610175578063bf2c5a07146108c8578063c762a46c146108dd578063c77b9de6146108f2578063c7f112e414610907578063c92577751461091c5761041b565b8063b0a7ef2914610889578063b184a3ae1461089e578063b3bc7844146107f6578063b69d0456146108b35761041b565b80638ea812c0116101ed5780638ea812c014610820578063a1b4d01114610835578063a2878cb11461084a578063a4792ab31461085f578063a734f06e146108745761041b565b8063819faf7b146107cc578063851954fa146107e15780638aea49d2146107f65780638bdb2afa1461080b5761041b565b80634226a9b9116103245780635ae51b82116102ac5780636cbc4a6e1161027b5780636cbc4a6e1461076357806375a8b0121461077857806375b5be2d1461078d5780637a88bdbd146107a25780637e09b9c2146107b75761041b565b80635ae51b821461070f5780635c0cb4791461072457806364ec4e5c1461073957806368e2a0141461074e5761041b565b80634a7101d5116102f35780634a7101d5146106a65780635187c091146106bb57806351f1985c146106d057806352a701b4146106e55780635aa8fb48146106fa5761041b565b80634226a9b9146105aa578063423d03f91461066757806344211d621461067c5780634752c680146106915761041b565b80632d3b5207116103a7578063372a26cb11610376578063372a26cb146105fe5780633ca5b234146106135780633e413bee146106285780633fc8cef31461063d57806340ab7b8c146106525761041b565b80632d3b5207146105aa5780632e707bd2146105bf5780632f48ab7d146105d457806334b4dabb146105e95761041b565b806313989140116103ee57806313989140146105415780631d209b65146105565780632113240d1461056b57806321a360f51461058057806322320c98146105955761041b565b806305d8aa0a1461042a578063085e2c5b1461045157806312dea160146104fb5780631388b4201461052c575b3332141561042857600080fd5b005b34801561043657600080fd5b5061043f610b4b565b60408051918252519081900360200190f35b34801561045d57600080fd5b506104a0600480360360a081101561047457600080fd5b506001600160a01b03813581169160208101359091169060408101359060608101359060800135610b52565b6040518083815260200180602001828103825283818151815260200191508051906020019060200280838360005b838110156104e65781810151838201526020016104ce565b50505050905001935050505060405180910390f35b34801561050757600080fd5b50610510610c9e565b604080516001600160a01b039092168252519081900360200190f35b34801561053857600080fd5b50610510610cb6565b34801561054d57600080fd5b5061043f610cce565b34801561056257600080fd5b5061043f610cd4565b34801561057757600080fd5b5061043f610cdc565b34801561058c57600080fd5b5061043f610ce2565b3480156105a157600080fd5b50610510610ceb565b3480156105b657600080fd5b5061043f610d03565b3480156105cb57600080fd5b5061043f610d0c565b3480156105e057600080fd5b50610510610d11565b3480156105f557600080fd5b5061043f610d23565b34801561060a57600080fd5b50610510610d28565b34801561061f57600080fd5b50610510610d40565b34801561063457600080fd5b50610510610d58565b34801561064957600080fd5b50610510610d6a565b34801561065e57600080fd5b50610510610d82565b34801561067357600080fd5b50610510610d9a565b34801561068857600080fd5b5061043f610db2565b34801561069d57600080fd5b5061043f610db7565b3480156106b257600080fd5b5061043f610dbf565b3480156106c757600080fd5b50610510610dc4565b3480156106dc57600080fd5b50610510610ddc565b3480156106f157600080fd5b50610510610df4565b34801561070657600080fd5b5061043f610e0c565b34801561071b57600080fd5b5061043f610e12565b34801561073057600080fd5b5061043f610e18565b34801561074557600080fd5b5061043f610e1d565b34801561075a57600080fd5b5061043f610e24565b34801561076f57600080fd5b5061043f610e2b565b34801561078457600080fd5b5061043f610e32565b34801561079957600080fd5b50610510610e38565b3480156107ae57600080fd5b5061043f610e4b565b3480156107c357600080fd5b5061043f610e50565b3480156107d857600080fd5b50610510610e57565b3480156107ed57600080fd5b50610510610e6f565b34801561080257600080fd5b5061043f610e87565b34801561081757600080fd5b50610510610e90565b34801561082c57600080fd5b5061043f610ea8565b34801561084157600080fd5b50610510610eb1565b34801561085657600080fd5b5061043f610ec9565b34801561086b57600080fd5b50610510610ed1565b34801561088057600080fd5b50610510610ee9565b34801561089557600080fd5b5061043f610f01565b3480156108aa57600080fd5b50610510610f07565b3480156108bf57600080fd5b50610510610f1f565b3480156108d457600080fd5b5061043f610f37565b3480156108e957600080fd5b5061043f610f3f565b3480156108fe57600080fd5b5061043f610f44565b34801561091357600080fd5b5061043f610f4a565b34801561092857600080fd5b50610510610f52565b34801561093d57600080fd5b5061043f610f6a565b34801561095257600080fd5b5061043f610f71565b34801561096757600080fd5b5061043f610f78565b34801561097c57600080fd5b5061043f610f7d565b34801561099157600080fd5b50610510610f85565b3480156109a657600080fd5b5061043f610f9d565b3480156109bb57600080fd5b50610510610fa4565b3480156109d057600080fd5b50610510610fbc565b3480156109e557600080fd5b5061043f610fd4565b610428600480360360c0811015610a0457600080fd5b6001600160a01b03823581169260208101359091169160408201359160608101359181019060a081016080820135640100000000811115610a4457600080fd5b820183602082011115610a5657600080fd5b80359060200191846020830284011164010000000083111715610a7857600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505091359250610fda915050565b348015610ac457600080fd5b5061043f61122c565b348015610ad957600080fd5b5061043f611234565b348015610aee57600080fd5b5061051061123c565b348015610b0357600080fd5b5061043f61124e565b348015610b1857600080fd5b50610510611253565b348015610b2d57600080fd5b5061043f61126b565b348015610b4257600080fd5b50610510611273565b6220000081565b600080546040805163085e2c5b60e01b81526001600160a01b03898116600483015288811660248301526044820188905260648201879052608482018690529151606093929092169163085e2c5b9160a4808201928792909190829003018186803b158015610bc057600080fd5b505afa158015610bd4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040908152811015610bfd57600080fd5b815160208301805160405192949293830192919084640100000000821115610c2457600080fd5b908301906020820185811115610c3957600080fd5b8251866020820283011164010000000082111715610c5657600080fd5b82525081516020918201928201910280838360005b83811015610c83578181015183820152602001610c6b565b50505050905001604052505050915091509550959350505050565b7352ae12abe5d8bd778bd5397f99ca900624cfadd481565b73794e6e91555438afc3ccf1c5076a74f42133d08d81565b61200081565b630400000081565b61800081565b64020000000081565b73a5407eae9ba41422680e2e00537571bcc53efbfd81565b64010000000081565b608081565b60008051602061491b83398151915281565b604081565b7379a8c46dea5ada233abaffd40f3a0a2b1e5a4f2781565b734fabb145d64652a948d72533023f6e7a623c7c5381565b6000805160206148fb83398151915281565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c81565b7345f783cce6b7ff23b2ab2d70e416cdb7d6055f5181565b601081565b631e00000081565b602081565b735c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f81565b73a2b47e3d5c44877cca798226b7b8118f9bfb7a5681565b738e870d67f660d95d5be530380d0ec0bd388289e181565b61400081565b61080081565b600881565b6202000081565b6210000081565b6208000081565b61040081565b6e085d4780b73119b644ae5ecd22b37681565b600281565b6240000081565b73398ec7346dcd622edc5ae82352f02be94c62d11981565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce31581565b64040000000081565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9581565b64080000000081565b734ddc2d193948926d02f9b1fe9e1daa0718270ed581565b634000000081565b737079e8517594e5b21d2b9a0d17cb33f5fe2bca7081565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b61100081565b7306364f10b501e868329afbc005b3492902d6c76381565b7306af07097c9eeb7fd685c692751d5c66db49c21581565b630800000081565b600181565b61020081565b638000000081565b7357ab1ec28d129707052df4df418d58a2d46d5f5181565b6280000081565b6204000081565b601281565b630200000081565b7352ea46506b9cc5ef470c5bf89f17dc28bb35d85c81565b6201000081565b73f6e2d7f616b67e46d708e4410746e9aab3a4c51881565b73818e6fecd516ecc3849daf6845e3ec868087b75581565b61010081565b846001600160a01b0316866001600160a01b03161415610ff957611224565b61100161485f565b6040518061024001604052806112828152602001611503815260200161168e81526020016119b38152602001611c8c8152602001611e178152602001611fdc81526020016121f58152602001612418815260200161263b81526020016127d981526020016129858152602001612af18152602001612c398152602001612c468152602001612c688152602001612c848152602001612ca081525090506012835111156110de5760405162461bcd60e51b815260040180806020018281038252604281526020018061499b6042913960600191505060405180910390fd5b600080805b855181101561113c5760008682815181106110fa57fe5b602002602001015111156111345761112e86828151811061111757fe5b602002602001015184612ec390919063ffffffff16565b92508091505b6001016110e3565b506000821161117c5760405162461bcd60e51b815260040180806020018281038252602f8152602001806148ab602f913960400191505060405180910390fd5b8660005b865181101561121e5786818151811061119557fe5b6020026020010151600014156111aa57611216565b60006111e2856111d68a85815181106111bf57fe5b60200260200101518d612f2690919063ffffffff16565b9063ffffffff612f7f16565b9050838214156111ef5750815b80830392506112138c8c8389866012811061120657fe5b602002015163ffffffff16565b50505b600101611180565b50505050505b505050505050565b631000000081565b632000000081565b60008051602061488b83398151915281565b600481565b733d9819210a31b4961b30ef54be2aed79b9c9cd3b81565b630100000081565b6000546001600160a01b031681565b6000816112976001600160a01b038616612fc1565b6113c757604080516303795fb160e11b81526001600160a01b0387166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b1580156112f957600080fd5b505afa15801561130d573d6000803e3d6000fd5b505050506040513d602081101561132357600080fd5b505190506001600160a01b038116156113c5576113408682612ffd565b604080516395e3c50b60e01b8152600481018490526001602482015242604482015290516001600160a01b038316916395e3c50b9160648083019260209291908290030181600087803b15801561139657600080fd5b505af11580156113aa573d6000803e3d6000fd5b505050506040513d60208110156113c057600080fd5b505191505b505b6113d9846001600160a01b0316612fc1565b6114f957604080516303795fb160e11b81526001600160a01b0386166004820152905160009173c0a47dfe034b400b47bdad5fecda2621de6c4d95916306f2bf6291602480820192602092909190829003018186803b15801561143b57600080fd5b505afa15801561144f573d6000803e3d6000fd5b505050506040513d602081101561146557600080fd5b505190506001600160a01b038116156114f757806001600160a01b031663f39b5b9b836001426040518463ffffffff1660e01b815260040180838152602001828152602001925050506020604051808303818588803b1580156114c757600080fd5b505af11580156114db573d6000803e3d6000fd5b50505050506040513d60208110156114f257600080fd5b505191505b505b90505b9392505050565b60006115238473818e6fecd516ecc3849daf6845e3ec868087b755612ffd565b73818e6fecd516ecc3849daf6845e3ec868087b7556329589f6161154f6001600160a01b038716612fc1565b61155a57600061155c565b835b61156e876001600160a01b0316612fc1565b611578578661158e565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b856115a1886001600160a01b0316612fc1565b6115ab57876115c1565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b604080516001600160e01b031960e088901b1681526001600160a01b039485166004820152602481019390935292166044820152306064820152600160ff1b6084820152600060a48201819052734d37f28d2db99e8d35a6c725a5f1749a085850a360c483015261010060e4830152610104820152905161014480830192602092919082900301818588803b15801561165957600080fd5b505af115801561166d573d6000803e3d6000fd5b50505050506040513d602081101561168457600080fd5b5051949350505050565b60006116a2846001600160a01b0316612fc1565b156117105773c0829421c1d260bd3cb3e0f06cfe2d52db2ce3156001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156116f657600080fd5b505af115801561170a573d6000803e3d6000fd5b50505050505b60007352ae12abe5d8bd778bd5397f99ca900624cfadd46001600160a01b031663bb34534c6040518163ffffffff1660e01b815260040180806c42616e636f724e6574776f726b60981b815250602001905060206040518083038186803b15801561177a57600080fd5b505afa15801561178e573d6000803e3d6000fd5b505050506040513d60208110156117a457600080fd5b5051905060606117b486866130b6565b90506117f16117cb876001600160a01b0316612fc1565b6117d557866117eb565b73c0829421c1d260bd3cb3e0f06cfe2d52db2ce3155b83612ffd565b6000826001600160a01b031663c7ba24bc838760016040518463ffffffff1660e01b81526004018080602001848152602001838152602001828103825285818151815260200191508051906020019060200280838360005b83811015611861578181015183820152602001611849565b50505050905001945050505050602060405180830381600087803b15801561188857600080fd5b505af115801561189c573d6000803e3d6000fd5b505050506040513d60208110156118b257600080fd5b505190506118c86001600160a01b038716612fc1565b156119a957604080516370a0823160e01b8152306004820152905173c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591632e1a7d4d9183916370a08231916024808301926020929190829003018186803b15801561192657600080fd5b505afa15801561193a573d6000803e3d6000fd5b505050506040513d602081101561195057600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561199057600080fd5b505af11580156119a4573d6000803e3d6000fd5b505050505b9695505050505050565b60006119c7846001600160a01b0316612fc1565b15611a355773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015611a1b57600080fd5b505af1158015611a2f573d6000803e3d6000fd5b50505050505b611a84611a4a856001600160a01b0316612fc1565b611a545784611a6a565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b73794e6e91555438afc3ccf1c5076a74f42133d08d612ffd565b600073794e6e91555438afc3ccf1c5076a74f42133d08d630621b4f6611ab26001600160a01b038816612fc1565b611abc5786611ad2565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b85611ae5886001600160a01b0316612fc1565b611aef5787611b05565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b604080516001600160e01b031960e087901b1681526001600160a01b03948516600482015260248101939093529216604482015260016064820152905160848083019260209291908290030181600087803b158015611b6357600080fd5b505af1158015611b77573d6000803e3d6000fd5b505050506040513d6020811015611b8d57600080fd5b50519050611ba36001600160a01b038516612fc1565b156114f957604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b158015611c0157600080fd5b505afa158015611c15573d6000803e3d6000fd5b505050506040513d6020811015611c2b57600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b158015611c6b57600080fd5b505af1158015611c7f573d6000803e3d6000fd5b5050505090509392505050565b6000806001600160a01b0385166000805160206148fb83398151915214611cb4576000611cb7565b60025b6001600160a01b03861660008051602061488b83398151915214611cdc576000611cdf565b60015b0160ff16905060006000805160206148fb8339815191526001600160a01b03861614611d0c576000611d0f565b60025b6001600160a01b03861660008051602061488b83398151915214611d34576000611d37565b60015b0160ff16905081600f0b60001480611d52575080600f0b6000145b15611d62576000925050506114fc565b611d808673a2b47e3d5c44877cca798226b7b8118f9bfb7a56612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a2b47e3d5c44877cca798226b7b8118f9bfb7a569263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b505af1158015611e0a573d6000803e3d6000fd5b5050505050509392505050565b6000806001600160a01b03851660008051602061491b83398151915214611e3f576000611e42565b60035b6001600160a01b0386166000805160206148fb83398151915214611e67576000611e6a565b60025b6001600160a01b03871660008051602061488b83398151915214611e8f576000611e92565b60015b010160ff169050600060008051602061491b8339815191526001600160a01b0316856001600160a01b031614611ec9576000611ecc565b60035b6001600160a01b0386166000805160206148fb83398151915214611ef1576000611ef4565b60025b6001600160a01b03871660008051602061488b83398151915214611f19576000611f1c565b60015b010160ff16905081600f0b60001480611f38575080600f0b6000145b15611f48576000925050506114fc565b611f66867352ea46506b9cc5ef470c5bf89f17dc28bb35d85c612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517352ea46506b9cc5ef470c5bf89f17dc28bb35d85c9263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b6000806001600160a01b0385166e085d4780b73119b644ae5ecd22b37614612005576000612008565b60045b6001600160a01b03861660008051602061491b8339815191521461202d576000612030565b60035b6001600160a01b0387166000805160206148fb83398151915214612055576000612058565b60025b6001600160a01b03881660008051602061488b8339815191521461207d576000612080565b60015b01010160ff16905060006e085d4780b73119b644ae5ecd22b3766001600160a01b0316856001600160a01b0316146120b95760006120bc565b60045b6001600160a01b03861660008051602061491b833981519152146120e15760006120e4565b60035b6001600160a01b0387166000805160206148fb8339815191521461210957600061210c565b60025b6001600160a01b03881660008051602061488b83398151915214612131576000612134565b60015b01010160ff16905081600f0b60001480612151575080600f0b6000145b15612161576000925050506114fc565b61217f867345f783cce6b7ff23b2ab2d70e416cdb7d6055f51612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517345f783cce6b7ff23b2ab2d70e416cdb7d6055f519263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b6000806001600160a01b038516734fabb145d64652a948d72533023f6e7a623c7c5314612223576000612226565b60045b6001600160a01b03861660008051602061491b8339815191521461224b57600061224e565b60035b6001600160a01b0387166000805160206148fb83398151915214612273576000612276565b60025b6001600160a01b03881660008051602061488b8339815191521461229b57600061229e565b60015b01010160ff1690506000734fabb145d64652a948d72533023f6e7a623c7c536001600160a01b0316856001600160a01b0316146122dc5760006122df565b60045b6001600160a01b03861660008051602061491b83398151915214612304576000612307565b60035b6001600160a01b0387166000805160206148fb8339815191521461232c57600061232f565b60025b6001600160a01b03881660008051602061488b83398151915214612354576000612357565b60015b01010160ff16905081600f0b60001480612374575080600f0b6000145b15612384576000925050506114fc565b6123a2867379a8c46dea5ada233abaffd40f3a0a2b1e5a4f27612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517379a8c46dea5ada233abaffd40f3a0a2b1e5a4f279263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b6000806001600160a01b0385167357ab1ec28d129707052df4df418d58a2d46d5f5114612446576000612449565b60045b6001600160a01b03861660008051602061491b8339815191521461246e576000612471565b60035b6001600160a01b0387166000805160206148fb83398151915214612496576000612499565b60025b6001600160a01b03881660008051602061488b833981519152146124be5760006124c1565b60015b01010160ff16905060007357ab1ec28d129707052df4df418d58a2d46d5f516001600160a01b0316856001600160a01b0316146124ff576000612502565b60045b6001600160a01b03861660008051602061491b8339815191521461252757600061252a565b60035b6001600160a01b0387166000805160206148fb8339815191521461254f576000612552565b60025b6001600160a01b03881660008051602061488b8339815191521461257757600061257a565b60015b01010160ff16905081600f0b60001480612597575080600f0b6000145b156125a7576000925050506114fc565b6125c58673a5407eae9ba41422680e2e00537571bcc53efbfd612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b602482015260448101869052600060648201819052915173a5407eae9ba41422680e2e00537571bcc53efbfd9263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b600061264f846001600160a01b0316612fc1565b61270857600061265e856137c0565b905061266a8582612ffd565b806001600160a01b031663a0712d68846040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b1580156126b057600080fd5b505af11580156126c4573d6000803e3d6000fd5b505050506040513d60208110156126da57600080fd5b50612700905081856126fb6001600160a01b0383163063ffffffff6139aa16565b611282565b9150506114fc565b61271a836001600160a01b0316612fc1565b6127cf576000612729846137c0565b90506000612738868386611282565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561278057600080fd5b505af1158015612794573d6000803e3d6000fd5b505050506040513d60208110156127aa57600080fd5b506127c690506001600160a01b0386163063ffffffff6139aa16565b925050506114fc565b5060009392505050565b60006001600160a01b03841660008051602061488b83398151915214156128bc57612818847306af07097c9eeb7fd685c692751d5c66db49c215612ffd565b60408051633b4da69f60e01b81523060048201526024810184905290517306af07097c9eeb7fd685c692751d5c66db49c21591633b4da69f91604480830192600092919082900301818387803b15801561287157600080fd5b505af1158015612885573d6000803e3d6000fd5b506128b592507306af07097c9eeb7fd685c692751d5c66db49c21591508590506126fb823063ffffffff6139aa16565b90506114fc565b6001600160a01b03831660008051602061488b83398151915214156127cf5760006128fc857306af07097c9eeb7fd685c692751d5c66db49c21585611282565b6040805163ef693bed60e01b81523060048201526024810183905290519192507306af07097c9eeb7fd685c692751d5c66db49c2159163ef693bed9160448082019260009290919082900301818387803b15801561295957600080fd5b505af115801561296d573d6000803e3d6000fd5b50612700925050506001600160a01b038516306139aa565b6000612999846001600160a01b0316612fc1565b612a575760006129a885613a54565b90506129b48582612ffd565b60408051636968703360e11b81526001600160a01b03871660048201526024810185905261044d6044820152905173398ec7346dcd622edc5ae82352f02be94c62d1199163d2d0e06691606480830192600092919082900301818387803b158015612a1e57600080fd5b505af1158015612a32573d6000803e3d6000fd5b5050505061270081856126fb30856001600160a01b03166139aa90919063ffffffff16565b612a69836001600160a01b0316612fc1565b6127cf576000612a7884613a54565b90506000612a87868386611282565b9050816001600160a01b031663db006a75826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015612acf57600080fd5b505af1158015612ae3573d6000803e3d6000fd5b5050505080925050506114fc565b600080737079e8517594e5b21d2b9a0d17cb33f5fe2bca706001600160a01b031663d4b839926040518163ffffffff1660e01b815260040160206040518083038186803b158015612b4157600080fd5b505afa158015612b55573d6000803e3d6000fd5b505050506040513d6020811015612b6b57600080fd5b50519050612b798582612ffd565b806001600160a01b031663fe029156612b9a876001600160a01b0316612fc1565b612ba5576000612ba7565b845b604080516001600160e01b031960e085901b1681526001600160a01b03808b1660048301528916602482015260448101889052600060648201529051608480830192602092919082900301818588803b158015612c0357600080fd5b505af1158015612c17573d6000803e3d6000fd5b50505050506040513d6020811015612c2e57600080fd5b505195945050505050565b60006114f9848484613e71565b60006114f98473c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2858561423a565b60006114f98460008051602061488b833981519152858561423a565b60006114f9846000805160206148fb833981519152858561423a565b6000806001600160a01b038516738e870d67f660d95d5be530380d0ec0bd388289e114612cce576000612cd1565b60045b6001600160a01b03861660008051602061491b83398151915214612cf6576000612cf9565b60035b6001600160a01b0387166000805160206148fb83398151915214612d1e576000612d21565b60025b6001600160a01b03881660008051602061488b83398151915214612d46576000612d49565b60015b01010160ff1690506000738e870d67f660d95d5be530380d0ec0bd388289e16001600160a01b0316856001600160a01b031614612d87576000612d8a565b60045b6001600160a01b03861660008051602061491b83398151915214612daf576000612db2565b60035b6001600160a01b0387166000805160206148fb83398151915214612dd7576000612dda565b60025b6001600160a01b03881660008051602061488b83398151915214612dff576000612e02565b60015b01010160ff16905081600f0b60001480612e1f575080600f0b6000145b15612e2f576000925050506114fc565b612e4d867306364f10b501e868329afbc005b3492902d6c763612ffd565b60408051635320bf6b60e11b8152600019808501600f90810b810b6004840152908401810b900b60248201526044810186905260006064820181905291517306364f10b501e868329afbc005b3492902d6c7639263a6417ed6926084808201939182900301818387803b158015611df657600080fd5b600082820183811015612f1d576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b600082612f3557506000612f20565b82820282848281612f4257fe5b0414612f1d5760405162461bcd60e51b81526004018080602001828103825260218152602001806148da6021913960400191505060405180910390fd5b6000612f1d83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061425a565b60006001600160a01b0382161580612ff557506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b90505b919050565b61300f826001600160a01b0316612fc1565b6130b25760408051636eb1769f60e11b81523060048201526001600160a01b038381166024830152915160ff9285169163dd62ed3e916044808301926020929190829003018186803b15801561306457600080fd5b505afa158015613078573d6000803e3d6000fd5b505050506040513d602081101561308e57600080fd5b5051901c6130b2576130b26001600160a01b0383168260001963ffffffff6142fc16565b5050565b6060816001600160a01b0316836001600160a01b031614156130e75750604080516000815260208101909152612f20565b6130f9836001600160a01b0316612fc1565b156131165773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31592505b613128826001600160a01b0316612fc1565b156131455773c0829421c1d260bd3cb3e0f06cfe2d52db2ce31591505b6001600160a01b038316731f573d6fb3f13d689ff844b4ce37794d79a7ff1c148061318c57506001600160a01b038216731f573d6fb3f13d689ff844b4ce37794d79a7ff1c145b156131b7576040805160038082526080820190925290602082016060803883390190505090506131d9565b60408051600580825260c08201909252906020820160a0803883390190505090505b6000806001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c146133a2576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6132366001600160a01b038b16612fc1565b6132405789613256565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b602083106132d45780518252601f1990920191602091820191016132b5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d8060008114613335576040519150601f19603f3d011682016040523d82523d6000602084013e61333a565b606091505b5091509150816133625760408051600080825260208201909252905b50945050505050612f20565b80806020019051602081101561337757600080fd5b505193506001600160a01b03841661339f576040805160008082526020820190925290613356565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14613560576000606073f6e2d7f616b67e46d708e4410746e9aab3a4c518612710636b625ad960e11b6133fc6001600160a01b038a16612fc1565b613406578861341c565b731f573d6fb3f13d689ff844b4ce37794d79a7ff1c5b604080516001600160a01b039092166024830152600060448084019190915281518084039091018152606490920181526020820180516001600160e01b03166001600160e01b0319909416939093178352518151919290918291908083835b6020831061349a5780518252601f19909201916020918201910161347b565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303818686fa925050503d80600081146134fb576040519150601f19603f3d011682016040523d82523d6000602084013e613500565b606091505b509150915081613520576040805160008082526020820190925290613356565b80806020019051602081101561353557600080fd5b505192506001600160a01b03831661355d576040805160008082526020820190925290613356565b50505b6001600160a01b038416731f573d6fb3f13d689ff844b4ce37794d79a7ff1c141561362357848360008151811061359357fe5b60200260200101906001600160a01b031690816001600160a01b03168152505081836001815181106135c157fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360028151811061360357fe5b6001600160a01b039092166020928302919091019091015250612f209050565b6001600160a01b038516731f573d6fb3f13d689ff844b4ce37794d79a7ff1c14156136c657731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360008151811061366a57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050808360018151811061369857fe5b60200260200101906001600160a01b031690816001600160a01b031681525050838360028151811061360357fe5b84836000815181106136d457fe5b60200260200101906001600160a01b031690816001600160a01b031681525050818360018151811061370257fe5b60200260200101906001600160a01b031690816001600160a01b031681525050731f573d6fb3f13d689ff844b4ce37794d79a7ff1c8360028151811061374457fe5b60200260200101906001600160a01b031690816001600160a01b031681525050808360038151811061377257fe5b60200260200101906001600160a01b031690816001600160a01b03168152505083836004815181106137a057fe5b6001600160a01b0390921660209283029190910190910152505092915050565b60006137d4826001600160a01b0316612fc1565b156137f45750734ddc2d193948926d02f9b1fe9e1daa0718270ed5612ff8565b6001600160a01b03821660008051602061488b833981519152141561382e5750735d3a536e4d6dbd6114cc1ead35777bab948e3643612ff8565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef141561386e5750736c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e612ff8565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e86214156138ae575073158079ee67fce2f58472a96584a73c7ab9ac95c1612ff8565b6001600160a01b0382166000805160206148fb83398151915214156138e857507339aa39c021dfbae8fac545936693ac917d5e7563612ff8565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415613928575073c11b1268c1a384e55c48c2391d8d480264a3a7f4612ff8565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f4981415613968575073b3319f5d18bc0d84dd1b4825dcde5d5f7266d407612ff8565b6001600160a01b03821660008051602061491b83398151915214156139a2575073f650c3d88d12db855b8bf7d11be6c55a4e07dcc9612ff8565b506000919050565b60006139b583612fc1565b156139cb57506001600160a01b03811631612f20565b826001600160a01b03166370a08231836040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b158015613a2157600080fd5b505afa158015613a35573d6000803e3d6000fd5b505050506040513d6020811015613a4b57600080fd5b50519050612f20565b6000613a68826001600160a01b0316612fc1565b15613a885750733a3a65aab0dd2a17e3f1947ba16138cd37d08c04612ff8565b6001600160a01b03821660008051602061488b8339815191521415613ac2575073fc1e690f61efd961294b3e1ce3313fbd8aa4f85d612ff8565b6001600160a01b0382166000805160206148fb8339815191521415613afc5750739ba00d6856a4edf4665bca2c2309936572473b7e612ff8565b6001600160a01b0382167357ab1ec28d129707052df4df418d58a2d46d5f511415613b3c575073625ae63000f46200499120b906716420bd059240612ff8565b6001600160a01b038216734fabb145d64652a948d72533023f6e7a623c7c531415613b7c5750736ee0f7bb50a54ab5253da0667b0dc2ee526c30a8612ff8565b6001600160a01b0382166e085d4780b73119b644ae5ecd22b3761415613bb75750734da9b813057d04baef4e5800e36083717b4a0341612ff8565b6001600160a01b03821660008051602061491b8339815191521415613bf157507371fc860f7d3a592a4a98740e39db31d25db65ae8612ff8565b6001600160a01b038216730d8775f648430679a709e98d2b0cb6250d2887ef1415613c31575073e1ba0fb44ccb0d11b80f92f4f8ed94ca3ff51d00612ff8565b6001600160a01b03821673dd974d5c2e2928dea5f71b9825b8b646686bd2001415613c715750739d91be44c06d373a8a226e1f3b146956083803eb612ff8565b6001600160a01b0382167380fb784b7ed66730e8b1dbd9820afd29931aab031415613cb15750737d2d3688df45ce7c552e19c27e007673da9204b8612ff8565b6001600160a01b03821673514910771af9ca656af840dff83e8264ecf986ca1415613cf1575073a64bd6c70cb9051f6a9ba1f163fdc07e0dfb5f84612ff8565b6001600160a01b038216730f5d2fb29fb7d3cfee444a200298f468908cc9421415613d315750736fce4a401b6b80ace52baaefe4421bd188e76f6f612ff8565b6001600160a01b038216739f8f72aa9304c8b593d555f12ef6589cc3a579a21415613d715750737deb5e830be29f91e298ba5ff1356bb7f8146998612ff8565b6001600160a01b038216731985365e9f78359a9b6ad760e32412f4a445e8621415613db157507371010a9d003445ac60c4e6a7017c1e89a477b438612ff8565b6001600160a01b03821673c011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f1415613df1575073328c4c80bc7aca0834db37e6600a6c49e12da4de612ff8565b6001600160a01b038216732260fac5e5542a773aa44fbcfedf7c193bc2c5991415613e31575073fc4b8ed459e00e5400be803a9bb3954234fd50e3612ff8565b6001600160a01b03821673e41d2489571d322189246dafa5ebde1f4699f49814156139a25750736fb0855c404e09c47c3fbca25f08d4e41f9f062f612ff8565b6000613e85846001600160a01b0316612fc1565b15613ef35773c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b158015613ed957600080fd5b505af1158015613eed573d6000803e3d6000fd5b50505050505b6000613f07856001600160a01b0316612fc1565b613f115784613f27565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b90506000613f3d856001600160a01b0316612fc1565b613f475784613f5d565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b6040805163e6a4390560e01b81526001600160a01b038581166004830152831660248201529051919250600091735c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f9163e6a43905916044808301926020929190829003018186803b158015613fc557600080fd5b505afa158015613fd9573d6000803e3d6000fd5b505050506040513d6020811015613fef57600080fd5b5051905061400e6001600160a01b03821684848863ffffffff6143d216565b935061402a6001600160a01b038416828763ffffffff61448916565b50816001600160a01b0316836001600160a01b031610156140c3576040805163022c0d9f60e01b815260006004820181905260248201879052306044830152608060648301526084820181905291516001600160a01b0384169263022c0d9f9260c4808201939182900301818387803b1580156140a657600080fd5b505af11580156140ba573d6000803e3d6000fd5b5050505061413d565b6040805163022c0d9f60e01b815260048101869052600060248201819052306044830152608060648301526084820181905291516001600160a01b0384169263022c0d9f9260c4808201939182900301818387803b15801561412457600080fd5b505af1158015614138573d6000803e3d6000fd5b505050505b61414f866001600160a01b0316612fc1565b1561423057604080516370a0823160e01b8152306004820152905173c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291632e1a7d4d9183916370a08231916024808301926020929190829003018186803b1580156141ad57600080fd5b505afa1580156141c1573d6000803e3d6000fd5b505050506040513d60208110156141d757600080fd5b5051604080516001600160e01b031960e085901b168152600481019290925251602480830192600092919082900301818387803b15801561421757600080fd5b505af115801561422b573d6000803e3d6000fd5b505050505b5050509392505050565b6000614251848461424c888887613e71565b613e71565b95945050505050565b600081836142e65760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156142ab578181015183820152602001614293565b50505050905090810190601f1680156142d85780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5060008385816142f257fe5b0495945050505050565b61430583612fc1565b6143cd57600081118015614393575060408051636eb1769f60e11b81523060048201526001600160a01b038481166024830152915160009286169163dd62ed3e916044808301926020929190829003018186803b15801561436557600080fd5b505afa158015614379573d6000803e3d6000fd5b505050506040513d602081101561438f57600080fd5b5051115b156143b3576143b36001600160a01b03841683600063ffffffff61450416565b6143cd6001600160a01b038416838363ffffffff61450416565b505050565b6000806143ee6001600160a01b0386168763ffffffff6139aa16565b9050600061440b6001600160a01b0386168863ffffffff6139aa16565b90506000614421856103e563ffffffff612f2616565b90506000614435828463ffffffff612f2616565b9050600061445b8361444f876103e863ffffffff612f2616565b9063ffffffff612ec316565b9050801561447857614473828263ffffffff612f7f16565b61447b565b60005b9a9950505050505050505050565b600081614498575060016114fc565b6144a184612fc1565b156144e2576040516001600160a01b0384169083156108fc029084906000818181858888f193505050501580156144dc573d6000803e3d6000fd5b506114fc565b6144fc6001600160a01b038516848463ffffffff61461716565b5060016114fc565b80158061458a575060408051636eb1769f60e11b81523060048201526001600160a01b03848116602483015291519185169163dd62ed3e91604480820192602092909190829003018186803b15801561455c57600080fd5b505afa158015614570573d6000803e3d6000fd5b505050506040513d602081101561458657600080fd5b5051155b6145c55760405162461bcd60e51b81526004018080602001828103825260368152602001806149656036913960400191505060405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526143cd908490614665565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526143cd9084905b614677826001600160a01b0316614823565b6146c8576040805162461bcd60e51b815260206004820152601f60248201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604482015290519081900360640190fd5b60006060836001600160a01b0316836040518082805190602001908083835b602083106147065780518252601f1990920191602091820191016146e7565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114614768576040519150601f19603f3d011682016040523d82523d6000602084013e61476d565b606091505b5091509150816147c4576040805162461bcd60e51b815260206004820181905260248201527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604482015290519081900360640190fd5b80511561481d578080602001905160208110156147e057600080fd5b505161481d5760405162461bcd60e51b815260040180806020018281038252602a81526020018061493b602a913960400191505060405180910390fd5b50505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061485757508115155b949350505050565b6040518061024001604052806012905b61488881526020019060019003908161486f5790505090565bfefe0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f4f6e6553706c69743a20646973747269627574696f6e2073686f756c6420636f6e7461696e206e6f6e2d7a65726f73536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec75361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565645361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f20746f206e6f6e2d7a65726f20616c6c6f77616e63654f6e6553706c69743a20446973747269627574696f6e2061727261792073686f756c64206e6f74206578636565642072657365727665732061727261792073697a65a265627a7a723158204781288028f83a88cf5954bcec8ff13b251aeb217faf21dd0b26af86c124971364736f6c63430005110032 \ No newline at end of file diff --git a/OneSplit.full.sol b/OneSplit.full.sol index e1bf74d..23162f5 100644 --- a/OneSplit.full.sol +++ b/OneSplit.full.sol @@ -6463,7 +6463,7 @@ contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswap uint256 optimalAmount = amounts[1 - leftoverIndex].mul( details.tokens[leftoverIndex].reserve - ) / details.tokens[1 - leftoverIndex].reserve; + ).div(details.tokens[1 - leftoverIndex].reserve); IERC20 _poolToken = poolToken; // stack too deep uint256 exchangeAmount = _calcRebalanceAmount( @@ -6472,7 +6472,7 @@ contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswap path[1].balanceOf(address(_poolToken)).add(amounts[1 - leftoverIndex]) ); - (bool success, bytes memory data) = address(uniswapRouter).staticcall.gas(100000)( + (bool success, bytes memory data) = address(uniswapRouter).staticcall.gas(200000)( abi.encodeWithSelector( uniswapRouter.getAmountsOut.selector, exchangeAmount, @@ -6494,7 +6494,7 @@ contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswap returnAmount = _addedLiquidity.add( amountsOutAfterSwap[1] // amountOut after swap .mul(_details.totalSupply.add(_addedLiquidity)) - .div(_details.tokens[leftoverIndex].reserve.sub(amountsOutAfterSwap[1])) + .div(_details.tokens[1 - leftoverIndex].reserve.sub(amountsOutAfterSwap[1])) ); return ( @@ -6681,7 +6681,10 @@ contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTo now.add(1800) ); - if (redeemAmounts[0] == amounts[0]) { + if ( + redeemAmounts[0] == amounts[0] && + redeemAmounts[1] == amounts[1] + ) { return; } @@ -6690,10 +6693,12 @@ contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTo path[0] = tokens[leftoverIndex]; path[1] = tokens[1 - leftoverIndex]; + address _poolToken = address(poolToken); // stack too deep + uint256 leftover = amounts[leftoverIndex].sub(redeemAmounts[leftoverIndex]); uint256 exchangeAmount = _calcRebalanceAmount( - amounts[leftoverIndex].sub(redeemAmounts[leftoverIndex]), - path[0].balanceOf(address(poolToken)), - path[1].balanceOf(address(poolToken)) + leftover, + path[0].balanceOf(_poolToken), + path[1].balanceOf(_poolToken) ); (bool success, bytes memory data) = address(uniswapRouter).call.gas(1000000)( @@ -6718,8 +6723,12 @@ contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTo uniswapRouter.addLiquidity.selector, tokens[0], tokens[1], - amountsOut[leftoverIndex], - amountsOut[1 - leftoverIndex], + leftoverIndex == 0 + ? leftover.sub(amountsOut[0]) + : amountsOut[1], + leftoverIndex == 1 + ? leftover.sub(amountsOut[0]) + : amountsOut[1], uint256(0), uint256(0), address(this), diff --git a/contracts/OneSplitUniswapV2PoolToken.sol b/contracts/OneSplitUniswapV2PoolToken.sol index 8e3252c..81c6289 100644 --- a/contracts/OneSplitUniswapV2PoolToken.sol +++ b/contracts/OneSplitUniswapV2PoolToken.sol @@ -277,7 +277,7 @@ contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswap path[1].balanceOf(address(_poolToken)).add(amounts[1 - leftoverIndex]) ); - (bool success, bytes memory data) = address(uniswapRouter).staticcall.gas(500000)( + (bool success, bytes memory data) = address(uniswapRouter).staticcall.gas(200000)( abi.encodeWithSelector( uniswapRouter.getAmountsOut.selector, exchangeAmount, @@ -299,7 +299,7 @@ contract OneSplitUniswapV2PoolTokenView is OneSplitViewWrapBase, OneSplitUniswap returnAmount = _addedLiquidity.add( amountsOutAfterSwap[1] // amountOut after swap .mul(_details.totalSupply.add(_addedLiquidity)) - .div(_details.tokens[leftoverIndex].reserve.sub(amountsOutAfterSwap[1])) + .div(_details.tokens[1 - leftoverIndex].reserve.sub(amountsOutAfterSwap[1])) ); return ( @@ -498,10 +498,12 @@ contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTo path[0] = tokens[leftoverIndex]; path[1] = tokens[1 - leftoverIndex]; + address _poolToken = address(poolToken); // stack too deep + uint256 leftover = amounts[leftoverIndex].sub(redeemAmounts[leftoverIndex]); uint256 exchangeAmount = _calcRebalanceAmount( - amounts[leftoverIndex].sub(redeemAmounts[leftoverIndex]), - path[0].balanceOf(address(poolToken)), - path[1].balanceOf(address(poolToken)) + leftover, + path[0].balanceOf(_poolToken), + path[1].balanceOf(_poolToken) ); (bool success, bytes memory data) = address(uniswapRouter).call.gas(1000000)( @@ -526,8 +528,12 @@ contract OneSplitUniswapV2PoolToken is OneSplitBaseWrap, OneSplitUniswapV2PoolTo uniswapRouter.addLiquidity.selector, tokens[0], tokens[1], - amountsOut[leftoverIndex], - amountsOut[1 - leftoverIndex], + leftoverIndex == 0 + ? leftover.sub(amountsOut[0]) + : amountsOut[1], + leftoverIndex == 1 + ? leftover.sub(amountsOut[0]) + : amountsOut[1], uint256(0), uint256(0), address(this),