From da28e648124c435e188dd28ae923f65bc71dcd98 Mon Sep 17 00:00:00 2001 From: deephil0226 Date: Mon, 27 Jun 2022 23:34:07 -0400 Subject: [PATCH 01/10] add velodrome exchange adapter --- .../interfaces/external/IVelodromeRouter.sol | 131 +++++++++++ .../exchange/VelodromeExchangeAdapter.sol | 120 ++++++++++ .../velodromeExchangeTradeModule.spec.ts | 213 ++++++++++++++++++ utils/deploys/deployAdapters.ts | 12 +- 4 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 contracts/interfaces/external/IVelodromeRouter.sol create mode 100644 contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol create mode 100644 test/integration/velodromeExchangeTradeModule.spec.ts diff --git a/contracts/interfaces/external/IVelodromeRouter.sol b/contracts/interfaces/external/IVelodromeRouter.sol new file mode 100644 index 000000000..30e506fc4 --- /dev/null +++ b/contracts/interfaces/external/IVelodromeRouter.sol @@ -0,0 +1,131 @@ +/* + Copyright 2020 Set Labs Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + SPDX-License-Identifier: Apache License, Version 2.0 +*/ + +pragma solidity 0.6.10; +pragma experimental "ABIEncoderV2"; + +interface IVelodromeRouter { + struct route { + address from; + address to; + bool stable; + } + + function factory() external pure returns (address); + function weth() external pure returns (address); + + function addLiquidity( + address tokenA, + address tokenB, + bool stable, + uint amountADesired, + uint amountBDesired, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) external returns (uint amountA, uint amountB, uint liquidity); + function addLiquidityETH( + address token, + bool stable, + uint amountTokenDesired, + uint amountTokenMin, + uint amountETHMin, + address to, + uint deadline + ) external payable returns (uint amountToken, uint amountETH, uint liquidity); + function removeLiquidity( + address tokenA, + address tokenB, + bool stable, + uint liquidity, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) external returns (uint amountA, uint amountB); + function removeLiquidityETH( + address token, + bool stable, + uint liquidity, + uint amountTokenMin, + uint amountETHMin, + address to, + uint deadline + ) external returns (uint amountToken, uint amountETH); + function removeLiquidityWithPermit( + address tokenA, + address tokenB, + bool stable, + uint liquidity, + uint amountAMin, + uint amountBMin, + address to, + uint deadline, + bool approveMax, uint8 v, bytes32 r, bytes32 s + ) external returns (uint amountA, uint amountB); + function removeLiquidityETHWithPermit( + address token, + bool stable, + uint liquidity, + uint amountTokenMin, + uint amountETHMin, + address to, + uint deadline, + bool approveMax, uint8 v, bytes32 r, bytes32 s + ) external returns (uint amountToken, uint amountETH); + function swapExactTokensForTokensSimple( + uint amountIn, + uint amountOutMin, + address tokenFrom, + address tokenTo, + bool stable, + address to, + uint deadline + ) external returns (uint[] memory amounts); + function swapExactTokensForTokens( + uint amountIn, + uint amountOutMin, + route[] calldata routes, + address to, + uint deadline + ) external returns (uint[] memory amounts); + function swapExactETHForTokens( + uint amountOutMin, + route[] calldata routes, + address to, + uint deadline + ) external payable returns (uint[] memory amounts); + + function getReserves(address tokenA, address tokenB, bool stable) external view returns (uint reserveA, uint reserveB); + function getAmountOut(uint amountIn, address tokenIn, address tokenOut) external view returns (uint amount, bool stable); + function getAmountsOut(uint amountIn, route[] memory routes) external view returns (uint[] memory amounts); + function quoteAddLiquidity( + address tokenA, + address tokenB, + bool stable, + uint amountADesired, + uint amountBDesired + ) external view returns (uint amountA, uint amountB, uint liquidity); + function quoteRemoveLiquidity( + address tokenA, + address tokenB, + bool stable, + uint liquidity + ) external view returns (uint amountA, uint amountB); +} \ No newline at end of file diff --git a/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol b/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol new file mode 100644 index 000000000..0b9b0073e --- /dev/null +++ b/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol @@ -0,0 +1,120 @@ +/* + Copyright 2022 Set Labs Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + SPDX-License-Identifier: Apache License, Version 2.0 +*/ + +pragma solidity 0.6.10; +pragma experimental "ABIEncoderV2"; + +import { IVelodromeRouter } from "../../../interfaces/external/IVelodromeRouter.sol"; + +/** + * @title VelodromeExchangeAdapter + * @author deephil + * + * Exchange adapter for Velodrome Exchange Router that encodes trade data + */ +contract VelodromeExchangeAdapter { + + /* ============ State Variables ============ */ + + // Address of Velodrome Exchange Router contract + address public immutable router; + + /* ============ Constructor ============ */ + + /** + * Set state variables + * + * @param _router Address of Velodrome Exchange Router contract + */ + constructor(address _router) public { + router = _router; + } + + /* ============ External Getter Functions ============ */ + + /** + * Return calldata for Velodrome Exchange Router + * + * @param _sourceToken Address of source token to be sold + * @param _destinationToken Address of destination token to buy + * @param _destinationAddress Address that assets should be transferred to + * @param _sourceQuantity Amount of source token to sell + * @param _minDestinationQuantity Min amount of destination token to buy + * @param _data Arbitrary bytes containing trade call data + * + * @return address Target contract address + * @return uint256 Call value + * @return bytes Trade calldata + */ + function getTradeCalldata( + address _sourceToken, + address _destinationToken, + address _destinationAddress, + uint256 _sourceQuantity, + uint256 _minDestinationQuantity, + bytes memory _data + ) + external + view + returns (address, uint256, bytes memory) + { + ( + IVelodromeRouter.route[] memory routes + ) = abi.decode(_data, (IVelodromeRouter.route[])); + + require(_sourceToken == routes[0].from, "Source token path mismatch"); + require(_destinationToken == routes[routes.length - 1].to, "Destination token path mismatch"); + + bytes memory callData = abi.encodeWithSelector( + IVelodromeRouter.swapExactTokensForTokens.selector, + _sourceQuantity, + _minDestinationQuantity, + routes, + _destinationAddress, + block.timestamp + ); + return (router, 0, callData); + } + + /** + * Returns the address to approve source tokens to for trading. This is the Velodrome Exchange Router address + * + * @return address Address of the contract to approve tokens to + */ + function getSpender() + external + view + returns (address) + { + return router; + } + + /** + * Generate data parameter to be passed to `getTradeCallData`. Returns encoded trade routes. + * + * @param _routes array of routes for Velodrome Router + * @return bytes Data parameter to be passed to `getTradeCallData` + */ + function generateDataParam(IVelodromeRouter.route[] calldata _routes) + external + pure + returns (bytes memory) + { + return abi.encode(_routes); + } +} \ No newline at end of file diff --git a/test/integration/velodromeExchangeTradeModule.spec.ts b/test/integration/velodromeExchangeTradeModule.spec.ts new file mode 100644 index 000000000..56a015288 --- /dev/null +++ b/test/integration/velodromeExchangeTradeModule.spec.ts @@ -0,0 +1,213 @@ +import "module-alias/register"; + +import { BigNumber } from "ethers"; +import { ethers, network } from "hardhat"; + +import { Address, Bytes } from "@utils/types"; +import { Account } from "@utils/test/types"; +import { + IERC20, + IERC20__factory, + IVelodromeRouter, + VelodromeExchangeAdapter, +} from "@typechain/index"; +import { SetToken, TradeModule, ManagerIssuanceHookMock } from "@utils/contracts"; +import { ZERO } from "@utils/constants"; +import DeployHelper from "@utils/deploys"; +import { ether, usdc } from "@utils/index"; +import { cacheBeforeEach, getAccounts, getSystemFixture, getWaffleExpect } from "@utils/test/index"; + +import { SystemFixture } from "@utils/fixtures"; +import { parseUnits } from "ethers/lib/utils"; + +const expect = getWaffleExpect(); + +describe("Velodrome TradeModule Integration [@optimism]", () => { + const velodromeAdapterName = "Velodrome"; + + let owner: Account; + let manager: Account; + let deployer: DeployHelper; + + let velodromeExchangeAdapter: VelodromeExchangeAdapter; + + let setup: SystemFixture; + let velodromeRouter: IVelodromeRouter; + let tradeModule: TradeModule; + + before(async () => { + [owner, manager] = await getAccounts(); + + deployer = new DeployHelper(owner.wallet); + setup = getSystemFixture(owner.address); + await setup.initialize(); + + velodromeRouter = await ethers.getContractAt( + "IVelodromeRouter", + "0xa132DAB612dB5cB9fC9Ac426A0Cc215A3423F9c9", + ); + + velodromeExchangeAdapter = await deployer.adapters.deployVelodromeExchangeAdapter( + velodromeRouter.address, + ); + + tradeModule = await deployer.modules.deployTradeModule(setup.controller.address); + await setup.controller.addModule(tradeModule.address); + + await setup.integrationRegistry.addIntegration( + tradeModule.address, + velodromeAdapterName, + velodromeExchangeAdapter.address, + ); + }); + + describe("#trade", function () { + let sourceToken: IERC20; + let destinationToken: IERC20; + let setToken: SetToken; + let issueQuantity: BigNumber; + + context("when trading a Default component on Velodrome", async () => { + let mockPreIssuanceHook: ManagerIssuanceHookMock; + let sourceTokenQuantity: BigNumber; + let destinationTokenQuantity: BigNumber; + + let subjectDestinationToken: Address; + let subjectSourceToken: Address; + let subjectSourceQuantity: BigNumber; + let subjectAdapterName: string; + let subjectSetToken: Address; + let subjectMinDestinationQuantity: BigNumber; + let subjectData: Bytes; + let subjectCaller: Account; + + cacheBeforeEach(async () => { + const whale = "0xAD7b4C162707E0B2b5f6fdDbD3f8538A5fbA0d60"; + // prepare WETH and USDC + await network.provider.request({ + method: "hardhat_impersonateAccount", + params: [whale], + }); + + await owner.wallet.sendTransaction({ + from: owner.address, + to: whale, + value: parseUnits("1"), + }); + + sourceToken = IERC20__factory.connect( + "0x4200000000000000000000000000000000000006", // weth + await ethers.getSigner(whale), + ); + destinationToken = IERC20__factory.connect( + "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", // usdc + await ethers.getSigner(whale), + ); + + // Create Set token + setToken = await setup.createSetToken( + [sourceToken.address], + [ether(1)], + [setup.issuanceModule.address, tradeModule.address], + manager.address, + ); + + await sourceToken.approve(velodromeRouter.address, ether(10)); + await destinationToken.approve(velodromeRouter.address, usdc(10000)); + + tradeModule = tradeModule.connect(manager.wallet); + await tradeModule.initialize(setToken.address); + + sourceTokenQuantity = ether(1); + [, destinationTokenQuantity] = await velodromeRouter.getAmountsOut(sourceTokenQuantity, [ + { + from: sourceToken.address, + to: destinationToken.address, + stable: false, + }, + ]); + + // Transfer from weth whale to manager + await sourceToken.transfer(manager.address, sourceTokenQuantity); + + // Approve tokens to Controller and call issue + sourceToken = sourceToken.connect(manager.wallet); + await sourceToken.approve(setup.issuanceModule.address, ethers.constants.MaxUint256); + + // Deploy mock issuance hook and initialize issuance module + setup.issuanceModule = setup.issuanceModule.connect(manager.wallet); + mockPreIssuanceHook = await deployer.mocks.deployManagerIssuanceHookMock(); + await setup.issuanceModule.initialize(setToken.address, mockPreIssuanceHook.address); + + issueQuantity = ether(1); + await setup.issuanceModule.issue(setToken.address, issueQuantity, owner.address); + }); + + beforeEach(async () => { + subjectSourceToken = sourceToken.address; + subjectDestinationToken = destinationToken.address; + subjectSourceQuantity = sourceTokenQuantity; + subjectSetToken = setToken.address; + subjectMinDestinationQuantity = destinationTokenQuantity.sub(usdc(1)); + subjectAdapterName = velodromeAdapterName; + + subjectData = await velodromeExchangeAdapter.generateDataParam([ + { + from: subjectSourceToken, + to: subjectDestinationToken, + stable: false, + }, + ]); + + subjectCaller = manager; + }); + + async function subject(): Promise { + tradeModule = tradeModule.connect(subjectCaller.wallet); + return tradeModule.trade( + subjectSetToken, + subjectAdapterName, + subjectSourceToken, + subjectSourceQuantity, + subjectDestinationToken, + subjectMinDestinationQuantity, + subjectData, + ); + } + + it("should transfer the correct components to the SetToken", async () => { + const oldDestinationTokenBalance = await destinationToken.balanceOf(setToken.address); + const [, expectedReceiveQuantity] = await velodromeRouter.getAmountsOut( + subjectSourceQuantity, + [ + { + from: subjectSourceToken, + to: subjectDestinationToken, + stable: false, + }, + ], + ); + + await subject(); + + const expectedDestinationTokenBalance = oldDestinationTokenBalance.add( + expectedReceiveQuantity, + ); + const newDestinationTokenBalance = await destinationToken.balanceOf(setToken.address); + expect(expectedReceiveQuantity).to.be.gt(ZERO); + expect(newDestinationTokenBalance).to.eq(expectedDestinationTokenBalance); + }); + + it("should transfer the correct components from the SetToken", async () => { + const oldSourceTokenBalance = await sourceToken.balanceOf(setToken.address); + + await subject(); + + const totalSourceQuantity = issueQuantity.mul(sourceTokenQuantity).div(ether(1)); + const expectedSourceTokenBalance = oldSourceTokenBalance.sub(totalSourceQuantity); + const newSourceTokenBalance = await sourceToken.balanceOf(setToken.address); + expect(newSourceTokenBalance).to.eq(expectedSourceTokenBalance); + }); + }); + }); +}); diff --git a/utils/deploys/deployAdapters.ts b/utils/deploys/deployAdapters.ts index 72af8e9d1..4716a0955 100644 --- a/utils/deploys/deployAdapters.ts +++ b/utils/deploys/deployAdapters.ts @@ -56,7 +56,11 @@ import { UniswapV3ExchangeAdapterV2__factory } from "../../typechain/factories/U import { SnapshotGovernanceAdapter__factory } from "../../typechain/factories/SnapshotGovernanceAdapter__factory"; import { SynthetixExchangeAdapter__factory } from "../../typechain/factories/SynthetixExchangeAdapter__factory"; import { CompoundBravoGovernanceAdapter__factory } from "../../typechain/factories/CompoundBravoGovernanceAdapter__factory"; -import { CompClaimAdapter__factory } from "../../typechain"; +import { + CompClaimAdapter__factory, + VelodromeExchangeAdapter, + VelodromeExchangeAdapter__factory, +} from "../../typechain"; import { RgtMigrationWrapAdapter__factory } from "../../typechain/factories/RgtMigrationWrapAdapter__factory"; export default class DeployAdapters { @@ -294,4 +298,10 @@ export default class DeployAdapters { exchange, ); } + + public async deployVelodromeExchangeAdapter( + velodromRouter: Address, + ): Promise { + return await new VelodromeExchangeAdapter__factory(this._deployerSigner).deploy(velodromRouter); + } } From bbdf241e7ce60875655048360e93296565f22b66 Mon Sep 17 00:00:00 2001 From: deephil0226 Date: Tue, 28 Jun 2022 22:16:43 -0400 Subject: [PATCH 02/10] add unit tests for VelodromeExchangeAdapter --- .../exchange/velodromeExchangeAdapter.spec.ts | 171 ++++++++++++++++++ utils/contracts/index.ts | 1 + 2 files changed, 172 insertions(+) create mode 100644 test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts diff --git a/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts b/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts new file mode 100644 index 000000000..5a46ebcd7 --- /dev/null +++ b/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts @@ -0,0 +1,171 @@ +import "module-alias/register"; + +import { BigNumber, ethers } from "ethers"; +import { utils } from "ethers"; + +import { Address, Bytes } from "@utils/types"; +import { Account } from "@utils/test/types"; +import { ZERO } from "@utils/constants"; +import { VelodromeExchangeAdapter } from "@utils/contracts"; +import DeployHelper from "@utils/deploys"; +import { ether } from "@utils/index"; +import { + addSnapshotBeforeRestoreAfterEach, + getAccounts, + getSystemFixture, + getWaffleExpect, + getLastBlockTimestamp, +} from "@utils/test/index"; +import { SystemFixture } from "@utils/fixtures"; +import { IVelodromeRouter__factory } from "@typechain/factories/IVelodromeRouter__factory"; + +const expect = getWaffleExpect(); +describe("VelodromeExchangeAdapter", () => { + const velodromRouterAddress = ethers.Wallet.createRandom().address; + + let owner: Account; + let mockSetToken: Account; + let deployer: DeployHelper; + let setup: SystemFixture; + let velodromeExchangeAdapter: VelodromeExchangeAdapter; + + before(async () => { + [owner, mockSetToken] = await getAccounts(); + + deployer = new DeployHelper(owner.wallet); + setup = getSystemFixture(owner.address); + await setup.initialize(); + + velodromeExchangeAdapter = await deployer.adapters.deployVelodromeExchangeAdapter( + velodromRouterAddress, + ); + }); + + addSnapshotBeforeRestoreAfterEach(); + + describe("constructor", async () => { + let subjectVelodromeRouter: Address; + + beforeEach(async () => { + subjectVelodromeRouter = velodromRouterAddress; + }); + + async function subject(): Promise { + return await deployer.adapters.deployVelodromeExchangeAdapter(subjectVelodromeRouter); + } + + it("should have the correct router address", async () => { + const deployedVelodromeV2ExchangeAdapter = await subject(); + + const actualRouterAddress = await deployedVelodromeV2ExchangeAdapter.router(); + expect(actualRouterAddress).to.eq(velodromRouterAddress); + }); + }); + + describe("getSpender", async () => { + async function subject(): Promise { + return await velodromeExchangeAdapter.getSpender(); + } + + it("should return the correct spender address", async () => { + const spender = await subject(); + + expect(spender).to.eq(velodromRouterAddress); + }); + }); + + describe("getTradeCalldata", async () => { + let sourceAddress: Address; + let destinationAddress: Address; + let sourceQuantity: BigNumber; + let destinationQuantity: BigNumber; + + let subjectMockSetToken: Address; + let subjectSourceToken: Address; + let subjectDestinationToken: Address; + let subjectSourceQuantity: BigNumber; + let subjectMinDestinationQuantity: BigNumber; + let subjectData: Bytes; + + beforeEach(async () => { + sourceAddress = setup.wbtc.address; // WBTC Address + sourceQuantity = BigNumber.from(100000000); // Trade 1 WBTC + destinationAddress = setup.dai.address; // DAI Address + destinationQuantity = ether(30000); // Receive at least 30k DAI + + subjectSourceToken = sourceAddress; + subjectDestinationToken = destinationAddress; + subjectMockSetToken = mockSetToken.address; + subjectSourceQuantity = sourceQuantity; + subjectMinDestinationQuantity = destinationQuantity; + }); + + async function subject(): Promise { + return await velodromeExchangeAdapter.getTradeCalldata( + subjectSourceToken, + subjectDestinationToken, + subjectMockSetToken, + subjectSourceQuantity, + subjectMinDestinationQuantity, + subjectData, + ); + } + + it("should return the correct trade calldata", async () => { + subjectData = await velodromeExchangeAdapter.generateDataParam([ + { + from: sourceAddress, + to: destinationAddress, + stable: false, + }, + ]); + const calldata = await subject(); + const callTimestamp = await getLastBlockTimestamp(); + const expectedCallData = IVelodromeRouter__factory.createInterface().encodeFunctionData( + "swapExactTokensForTokens", + [ + sourceQuantity, + destinationQuantity, + [{ from: sourceAddress, to: destinationAddress, stable: false }], + subjectMockSetToken, + callTimestamp, + ], + ); + expect(JSON.stringify(calldata)).to.eq( + JSON.stringify([velodromRouterAddress, ZERO, expectedCallData]), + ); + }); + + describe("when passed in custom path to trade data", async () => { + beforeEach(async () => { + const path = [sourceAddress, setup.weth.address, destinationAddress]; + subjectData = utils.defaultAbiCoder.encode(["address[]"], [path]); + }); + + it("should return the correct trade calldata", async () => { + subjectData = await velodromeExchangeAdapter.generateDataParam([ + { from: sourceAddress, to: setup.weth.address, stable: false }, + { from: setup.weth.address, to: destinationAddress, stable: false }, + ]); + const calldata = await subject(); + const callTimestamp = await getLastBlockTimestamp(); + const expectedCallData = IVelodromeRouter__factory.createInterface().encodeFunctionData( + "swapExactTokensForTokens", + [ + sourceQuantity, + destinationQuantity, + [ + { from: sourceAddress, to: setup.weth.address, stable: false }, + { from: setup.weth.address, to: destinationAddress, stable: false }, + ], + subjectMockSetToken, + callTimestamp, + ], + ); + expect(JSON.stringify(calldata)).to.eq( + JSON.stringify([velodromRouterAddress, ZERO, expectedCallData]), + ); + }); + }); + }); +}); diff --git a/utils/contracts/index.ts b/utils/contracts/index.ts index 593889b16..25ca15889 100644 --- a/utils/contracts/index.ts +++ b/utils/contracts/index.ts @@ -109,6 +109,7 @@ export { Uni } from "../../typechain/Uni"; export { UniswapPairPriceAdapter } from "../../typechain/UniswapPairPriceAdapter"; export { UniswapV2AmmAdapter } from "../../typechain/UniswapV2AmmAdapter"; export { UniswapV2ExchangeAdapter } from "../../typechain/UniswapV2ExchangeAdapter"; +export { VelodromeExchangeAdapter } from "../../typechain/VelodromeExchangeAdapter"; export { UniswapV2ExchangeAdapterV2 } from "../../typechain/UniswapV2ExchangeAdapterV2"; export { UniswapV2IndexExchangeAdapter } from "../../typechain/UniswapV2IndexExchangeAdapter"; export { UniswapV3IndexExchangeAdapter } from "../../typechain/UniswapV3IndexExchangeAdapter"; From 699e744aba795e593e71357e7017fddfe5cbb9c1 Mon Sep 17 00:00:00 2001 From: deephil0226 Date: Wed, 29 Jun 2022 07:44:20 -0400 Subject: [PATCH 03/10] update unit test to use velodrome fixture --- .../interfaces/external/IVelodromeRouter.sol | 18 - .../exchange/VelodromeExchangeAdapter.sol | 4 +- external/abi/velodrome/VelodromeFactory.json | 8 + external/abi/velodrome/VelodromePair.json | 8 + external/abi/velodrome/VelodromeRouter.json | 8 + .../contracts/velodrome/VelodromeFactory.sol | 104 ++++ .../contracts/velodrome/VelodromePair.sol | 559 ++++++++++++++++++ .../contracts/velodrome/VelodromeRouter.sol | 427 +++++++++++++ .../contracts/velodrome/interfaces/IERC20.sol | 16 + .../interfaces/IVelodromeFactory.sol | 10 + .../velodrome/interfaces/IVelodromePair.sol | 15 + .../interfaces/IVelodromePairCallee.sol | 6 + .../velodrome/interfaces/IVelodromeRouter.sol | 6 + .../contracts/velodrome/interfaces/IWETH.sol | 8 + external/contracts/velodrome/lib/Math.sol | 35 ++ .../exchange/velodromeExchangeAdapter.spec.ts | 108 ++-- utils/contracts/velodrome.ts | 8 + utils/deploys/dependencies.ts | 5 + utils/deploys/deployExternal.ts | 23 + utils/fixtures/velodromeFixture.ts | 66 +++ utils/test/index.ts | 2 + 21 files changed, 1391 insertions(+), 53 deletions(-) create mode 100644 external/abi/velodrome/VelodromeFactory.json create mode 100644 external/abi/velodrome/VelodromePair.json create mode 100644 external/abi/velodrome/VelodromeRouter.json create mode 100644 external/contracts/velodrome/VelodromeFactory.sol create mode 100644 external/contracts/velodrome/VelodromePair.sol create mode 100644 external/contracts/velodrome/VelodromeRouter.sol create mode 100644 external/contracts/velodrome/interfaces/IERC20.sol create mode 100644 external/contracts/velodrome/interfaces/IVelodromeFactory.sol create mode 100644 external/contracts/velodrome/interfaces/IVelodromePair.sol create mode 100644 external/contracts/velodrome/interfaces/IVelodromePairCallee.sol create mode 100644 external/contracts/velodrome/interfaces/IVelodromeRouter.sol create mode 100644 external/contracts/velodrome/interfaces/IWETH.sol create mode 100644 external/contracts/velodrome/lib/Math.sol create mode 100644 utils/contracts/velodrome.ts create mode 100644 utils/fixtures/velodromeFixture.ts diff --git a/contracts/interfaces/external/IVelodromeRouter.sol b/contracts/interfaces/external/IVelodromeRouter.sol index 30e506fc4..811a51d0e 100644 --- a/contracts/interfaces/external/IVelodromeRouter.sol +++ b/contracts/interfaces/external/IVelodromeRouter.sol @@ -1,21 +1,3 @@ -/* - Copyright 2020 Set Labs Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - SPDX-License-Identifier: Apache License, Version 2.0 -*/ - pragma solidity 0.6.10; pragma experimental "ABIEncoderV2"; diff --git a/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol b/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol index 0b9b0073e..8e49d227a 100644 --- a/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol +++ b/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol @@ -76,7 +76,8 @@ contract VelodromeExchangeAdapter { ( IVelodromeRouter.route[] memory routes ) = abi.decode(_data, (IVelodromeRouter.route[])); - + + require(routes.length > 0, "empty routes"); require(_sourceToken == routes[0].from, "Source token path mismatch"); require(_destinationToken == routes[routes.length - 1].to, "Destination token path mismatch"); @@ -115,6 +116,7 @@ contract VelodromeExchangeAdapter { pure returns (bytes memory) { + require(_routes.length > 0, "empty routes"); return abi.encode(_routes); } } \ No newline at end of file diff --git a/external/abi/velodrome/VelodromeFactory.json b/external/abi/velodrome/VelodromeFactory.json new file mode 100644 index 000000000..cae4e1b5b --- /dev/null +++ b/external/abi/velodrome/VelodromeFactory.json @@ -0,0 +1,8 @@ +{ + "contractName": "VelodromeFactory", + "abi": [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"bool","name":"stable","type":"bool"},{"indexed":false,"internalType":"address","name":"pair","type":"address"},{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"PairCreated","type":"event"},{"inputs":[],"name":"MAX_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptFeeManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptPauser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allPairs","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allPairsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"}],"name":"createPair","outputs":[{"internalType":"address","name":"pair","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_stable","type":"bool"}],"name":"getFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInitializable","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"}],"name":"getPair","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isPair","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pairCodeHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"pauser","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingFeeManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingPauser","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_stable","type":"bool"},{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeManager","type":"address"}],"name":"setFeeManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_state","type":"bool"}],"name":"setPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pauser","type":"address"}],"name":"setPauser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stableFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"volatileFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}], + "bytecode": "0x608060405234801561001057600080fd5b50600080546001600160a81b03191633610100810260ff1916919091178255600480546001600160a01b0319169091179055600280805560035561519190819061005a90396000f3fe60806040523480156200001157600080fd5b50600436106200015d5760003560e01c80639a7165e411620000c7578063bedb86fb1162000086578063bedb86fb14620002da578063d0fb020314620002f1578063e1f76b441462000305578063e5e31b13146200031c578063eb13c4cf1462000342578063f94c53c7146200037b57600080fd5b80639a7165e4146200027b5780639aab9248146200028f5780639fd0506d1462000299578063b187bd2614620002b2578063bc063e1a14620002d157600080fd5b80635084ed0311620001205780635084ed0314620001e9578063512b45ea14620001f3578063574f2ba3146200020a5780636801cc30146200021357806382dfdce414620002505780638a4fa0d2146200026757600080fd5b8063167a6f9014620001625780631e3dd18b146200016e5780632d88af4a14620001a257806340bbd77514620001b9578063472d35b914620001d2575b600080fd5b6200016c62000385565b005b620001856200017f36600462000987565b620003c8565b6040516001600160a01b0390911681526020015b60405180910390f35b6200016c620001b3366004620009be565b620003f3565b620001c360025481565b60405190815260200162000199565b6200016c620001e3366004620009be565b62000432565b620001c360035481565b620001c362000204366004620009f4565b620004a6565b600754620001c3565b620001856200022436600462000a12565b60066020908152600093845260408085208252928452828420905282529020546001600160a01b031681565b620001856200026136600462000a12565b620004c1565b60055462000185906001600160a01b031681565b60015462000185906001600160a01b031681565b620001c3620007ad565b600054620001859061010090046001600160a01b031681565b600054620002c09060ff1681565b604051901515815260200162000199565b620001c3600581565b6200016c620002eb366004620009f4565b620007e1565b60045462000185906001600160a01b031681565b6200016c6200031636600462000a5c565b62000811565b620002c06200032d366004620009be565b60086020526000908152604090205460ff1681565b600954600a54604080516001600160a01b0393841681529282166020840152600160a01b90910460ff1615159082015260600162000199565b6200016c620008f9565b6001546001600160a01b031633146200039d57600080fd5b600154600080546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b60078181548110620003d957600080fd5b6000918252602090912001546001600160a01b0316905081565b60005461010090046001600160a01b031633146200041057600080fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6004546001600160a01b03163314620004845760405162461bcd60e51b815260206004820152600f60248201526e3737ba103332b29036b0b730b3b2b960891b60448201526064015b60405180910390fd5b600580546001600160a01b0319166001600160a01b0392909216919091179055565b600081620004b757600354620004bb565b6002545b92915050565b6000826001600160a01b0316846001600160a01b031614156200050c5760405162461bcd60e51b8152602060048201526002602482015261494160f01b60448201526064016200047b565b600080846001600160a01b0316866001600160a01b0316106200053157848662000534565b85855b90925090506001600160a01b038216620005765760405162461bcd60e51b81526020600482015260026024820152615a4160f01b60448201526064016200047b565b6001600160a01b0382811660009081526006602090815260408083208585168452825280832088151584529091529020541615620005dc5760405162461bcd60e51b8152602060048201526002602482015261504560f01b60448201526064016200047b565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b16603482015284151560f81b604882015260009060490160408051601f19818403018152908290528051602090910120600a80546001600160a01b038087166001600160a01b03198b1515600160a01b0281166001600160a81b03199094169390931717909255600980549288169290911691909117905591508190620006839062000979565b8190604051809103906000f5905080158015620006a4573d6000803e3d6000fd5b506001600160a01b0384811660008181526006602081815260408084208987168086529083528185208d15158087529084528286208054988a166001600160a01b0319998a16811790915582875294845282862087875284528286208187528452828620805489168617905560078054600181810183557fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6889091018054909a1687179099558587526008855295839020805460ff1916909817909755935481519687529186019290925290840152929650907fc4805696c66d7cf352fc1d6bb633ad5ee82f6cb577c453024b6e0eb8306c6fc99060600160405180910390a35050509392505050565b600060405180602001620007c19062000979565b6020820181038252601f19601f8201166040525080519060200120905090565b60005461010090046001600160a01b03163314620007fe57600080fd5b6000805460ff1916911515919091179055565b6004546001600160a01b031633146200085f5760405162461bcd60e51b815260206004820152600f60248201526e3737ba103332b29036b0b730b3b2b960891b60448201526064016200047b565b6005811115620008a15760405162461bcd60e51b815260206004820152600c60248201526b0cccaca40e8dede40d0d2ced60a31b60448201526064016200047b565b80620008e65760405162461bcd60e51b8152602060048201526013602482015272666565206d757374206265206e6f6e7a65726f60681b60448201526064016200047b565b8115620008f35760025550565b60035550565b6005546001600160a01b03163314620009555760405162461bcd60e51b815260206004820152601760248201527f6e6f742070656e64696e6720666565206d616e6167657200000000000000000060448201526064016200047b565b600554600480546001600160a01b0319166001600160a01b03909216919091179055565b6146d28062000a8a83390190565b6000602082840312156200099a57600080fd5b5035919050565b80356001600160a01b0381168114620009b957600080fd5b919050565b600060208284031215620009d157600080fd5b620009dc82620009a1565b9392505050565b80358015158114620009b957600080fd5b60006020828403121562000a0757600080fd5b620009dc82620009e3565b60008060006060848603121562000a2857600080fd5b62000a3384620009a1565b925062000a4360208501620009a1565b915062000a5360408501620009e3565b90509250925092565b6000806040838503121562000a7057600080fd5b62000a7b83620009e3565b94602093909301359350505056fe61016060405260006002556000600d556000600e5560016013553480156200002657600080fd5b50336001600160a01b0316610100816001600160a01b0316815250506000806000336001600160a01b031663eb13c4cf6040518163ffffffff1660e01b8152600401606060405180830381865afa15801562000086573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ac9190620007c8565b8015156080526001600160a01b0380831660c052831660a052604051929550909350915083908390620000df90620006f7565b6001600160a01b03928316815291166020820152604001604051809103906000f08015801562000113573d6000803e3d6000fd5b506001600160a01b031660e05280156200034557826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000166573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000190919081019062000863565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620001cf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620001f9919081019062000863565b6040516020016200020c9291906200091b565b604051602081830303815290604052600090805190602001906200023292919062000705565b50826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000272573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526200029c919081019062000863565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620002db573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000305919081019062000863565b6040516020016200031892919062000976565b604051602081830303815290604052600190805190602001906200033e92919062000705565b506200055e565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000384573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620003ae919081019062000863565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620003ed573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000417919081019062000863565b6040516020016200042a929190620009c7565b604051602081830303815290604052600090805190602001906200045092919062000705565b50826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000490573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620004ba919081019062000863565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620004f9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000523919081019062000863565b6040516020016200053692919062000a24565b604051602081830303815290604052600190805190602001906200055c92919062000705565b505b826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200059d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620005c3919062000a46565b620005d090600a62000b87565b6101208181525050816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000617573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200063d919062000a46565b6200064a90600a62000b87565b6101405250506040805160608101825242815260006020820181815292820181815260078054600181018255925291517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68860039092029182015591517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c689830155517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68a909101555062000bd5565b610370806200436283390190565b828054620007139062000b98565b90600052602060002090601f01602090048101928262000737576000855562000782565b82601f106200075257805160ff191683800117855562000782565b8280016001018555821562000782579182015b828111156200078257825182559160200191906001019062000765565b506200079092915062000794565b5090565b5b8082111562000790576000815560010162000795565b80516001600160a01b0381168114620007c357600080fd5b919050565b600080600060608486031215620007de57600080fd5b620007e984620007ab565b9250620007f960208501620007ab565b9150604084015180151581146200080f57600080fd5b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b60005b838110156200084d57818101518382015260200162000833565b838111156200085d576000848401525b50505050565b6000602082840312156200087657600080fd5b81516001600160401b03808211156200088e57600080fd5b818401915084601f830112620008a357600080fd5b815181811115620008b857620008b86200081a565b604051601f8201601f19908116603f01168101908382118183101715620008e357620008e36200081a565b81604052828152876020848701011115620008fd57600080fd5b6200091083602083016020880162000830565b979650505050505050565b6e029ba30b13632ab189020a6a690169608d1b8152600083516200094781600f85016020880162000830565b602f60f81b600f9184019182015283516200096a81601084016020880162000830565b01601001949350505050565b6473414d4d2d60d81b8152600083516200099881600585016020880162000830565b602f60f81b6005918401918201528351620009bb81600684016020880162000830565b01600601949350505050565b7002b37b630ba34b632ab189020a6a690169607d1b815260008351620009f581601185016020880162000830565b602f60f81b601191840191820152835162000a1881601284016020880162000830565b01601201949350505050565b6476414d4d2d60d81b8152600083516200099881600585016020880162000830565b60006020828403121562000a5957600080fd5b815160ff8116811462000a6b57600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b600181815b8085111562000ac957816000190482111562000aad5762000aad62000a72565b8085161562000abb57918102915b93841c939080029062000a8d565b509250929050565b60008262000ae25750600162000b81565b8162000af15750600062000b81565b816001811462000b0a576002811462000b155762000b35565b600191505062000b81565b60ff84111562000b295762000b2962000a72565b50506001821b62000b81565b5060208310610133831016604e8410600b841016171562000b5a575081810a62000b81565b62000b66838362000a88565b806000190482111562000b7d5762000b7d62000a72565b0290505b92915050565b600062000a6b60ff84168362000ad1565b600181811c9082168062000bad57607f821691505b6020821081141562000bcf57634e487b7160e01b600052602260045260246000fd5b50919050565b60805160a05160c05160e05161010051610120516101405161360c62000d5660003960008181610431015281816125340152818161281e015281816128e001526129eb01526000818161040e015281816124f3015281816127df0152818161292201526129c50152600081816107b801528181610c0701528181610cd801526120630152600081816105f201528181611c770152818161235a01526124270152600081816104bb0152818161064701528181610714015281816108f301528181610b9b01528181611510015281816116f701528181611b1e015281816121c001526124060152600081816102f90152818161049301528181610622015281816108d201528181610b7a0152818161147a015281816116d501528181611afc015281816121380152818161233901528181612860015281816128a70152818161298c0152612a2f0152600081816103970152818161046301528181610bcf01528181610ca001528181612032015281816124cb01526127ab015261360c6000f3fe608060405234801561001057600080fd5b50600436106102745760003560e01c80637ecebe0011610151578063bda39cad116100c3578063d294f09311610087578063d294f09314610736578063d505accf1461073e578063dd62ed3e14610751578063ebeb31db1461077c578063f140a35a14610784578063fff6cae91461079757600080fd5b8063bda39cad146106eb578063bf944dbc146106f4578063c245febc146106fd578063c5700a0214610706578063d21220a71461070f57600080fd5b80639d63848a116101155780639d63848a146106145780639e8cc04b146106725780639f767c8814610685578063a1ac4d13146106a5578063a9059cbb146106c5578063bc25cf77146106d857600080fd5b80637ecebe001461057357806389afcb44146105935780638a7b8cf2146105bb57806395d89b41146105e55780639af1d35a146105ed57600080fd5b8063252c09d7116101ea5780634d5a9f8a116101ae5780634d5a9f8a146104f1578063517b3f82146105115780635881c475146105245780635a76f25e146105375780636a6278421461054057806370a082311461055357600080fd5b8063252c09d7146103cc578063313ce567146103df57806332c0defd146103f9578063392f37e914610402578063443cb4bc146104e857600080fd5b806313345fe11161023c57806313345fe11461033357806318160ddd146103535780631df8c7171461036a578063205aabf11461037257806322be3de11461039257806323b872dd146103b957600080fd5b8063022c0d9f1461027957806306fdde031461028e5780630902f1ac146102ac578063095ea7b3146102d15780630dfe1681146102f4575b600080fd5b61028c61028736600461308b565b61079f565b005b610296610eec565b6040516102a3919061314f565b60405180910390f35b600854600954600a545b604080519384526020840192909252908201526060016102a3565b6102e46102df366004613182565b610f7a565b60405190151581526020016102a3565b61031b7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016102a3565b6103466103413660046131ac565b610fe7565b6040516102a391906131e5565b61035c60025481565b6040519081526020016102a3565b6102b66111e3565b61035c610380366004613229565b60106020526000908152604090205481565b6102e47f000000000000000000000000000000000000000000000000000000000000000081565b6102e46103c7366004613244565b611252565b6102b66103da366004613280565b61131b565b6103e7601281565b60405160ff90911681526020016102a3565b61035c600d5481565b600854600954604080517f000000000000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060208201529081019290925260608201527f0000000000000000000000000000000000000000000000000000000000000000151560808201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660a08301527f00000000000000000000000000000000000000000000000000000000000000001660c082015260e0016102a3565b61035c60085481565b61035c6104ff366004613229565b60116020526000908152604090205481565b61035c61051f366004613182565b61134e565b610346610532366004613299565b611437565b61035c60095481565b61035c61054e366004613229565b611446565b61035c610561366004613229565b60046020526000908152604090205481565b61035c610581366004613229565b60066020526000908152604090205481565b6105a66105a1366004613229565b6116a3565b604080519283526020830191909152016102a3565b6105c36119c0565b60408051825181526020808401519082015291810151908201526060016102a3565b610296611a40565b61031b7f000000000000000000000000000000000000000000000000000000000000000081565b604080516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000166020820152016102a3565b61035c610680366004613299565b611a4d565b61035c610693366004613229565b600f6020526000908152604090205481565b61035c6106b3366004613229565b60126020526000908152604090205481565b6102e46106d3366004613182565b611aba565b61028c6106e6366004613229565b611ad0565b61035c600e5481565b61035c600b5481565b61035c600c5481565b61035c600a5481565b61031b7f000000000000000000000000000000000000000000000000000000000000000081565b6105a6611bee565b61028c61074c3660046132cc565b611d15565b61035c61075f36600461333f565b600360209081526000928352604080842090915290825290205481565b60075461035c565b61035c610792366004613372565b61201d565b61028c61210b565b6013546001146107ae57600080fd5b60026013819055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b187bd266040518163ffffffff1660e01b8152600401602060405180830381865afa158015610814573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108389190613395565b1561084257600080fd5b60008511806108515750600084115b6108885760405162461bcd60e51b8152602060048201526003602482015262494f4160e81b60448201526064015b60405180910390fd5b600854600954818710801561089c57508086105b6108cd5760405162461bcd60e51b8152602060048201526002602482015261125360f21b604482015260640161087f565b6000807f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03898116908316148015906109405750806001600160a01b0316896001600160a01b031614155b6109715760405162461bcd60e51b8152602060048201526002602482015261125560f21b604482015260640161087f565b8a1561098257610982828a8d612245565b891561099357610993818a8c612245565b8615610a0057604051639a7bff7960e01b81526001600160a01b038a1690639a7bff79906109cd9033908f908f908e908e906004016133b7565b600060405180830381600087803b1580156109e757600080fd5b505af11580156109fb573d6000803e3d6000fd5b505050505b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015610a44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a689190613403565b6040516370a0823160e01b81523060048201529094506001600160a01b038216906370a0823190602401602060405180830381865afa158015610aaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad39190613403565b9250505060008985610ae59190613432565b8311610af2576000610b06565b610afc8a86613432565b610b069084613432565b90506000610b148a86613432565b8311610b21576000610b35565b610b2b8a86613432565b610b359084613432565b90506000821180610b465750600081115b610b785760405162461bcd60e51b815260206004820152600360248201526249494160e81b604482015260640161087f565b7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008315610c8b57604051632895a2f560e11b81527f000000000000000000000000000000000000000000000000000000000000000015156004820152610c8b90612710906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063512b45ea90602401602060405180830381865afa158015610c4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c729190613403565b610c7c9087613449565b610c869190613468565b612334565b8215610d5c57604051632895a2f560e11b81527f000000000000000000000000000000000000000000000000000000000000000015156004820152610d5c90612710906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063512b45ea90602401602060405180830381865afa158015610d1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d439190613403565b610d4d9086613449565b610d579190613468565b612401565b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015610da0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc49190613403565b6040516370a0823160e01b81523060048201529096506001600160a01b038216906370a0823190602401602060405180830381865afa158015610e0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2f9190613403565b9450610e3b88886124c7565b610e4587876124c7565b1015610e775760405162461bcd60e51b81526020600482015260016024820152604b60f81b604482015260640161087f565b5050610e8584848888612613565b60408051838152602081018390529081018c9052606081018b90526001600160a01b038a169033907fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d8229060800160405180910390a350506001601355505050505050505050565b60008054610ef99061348a565b80601f0160208091040260200160405190810160405280929190818152602001828054610f259061348a565b8015610f725780601f10610f4757610100808354040283529160200191610f72565b820191906000526020600020905b815481529060010190602001808311610f5557829003601f168201915b505050505081565b3360008181526003602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610fd59086815260200190565b60405180910390a35060015b92915050565b606060008367ffffffffffffffff811115611004576110046134bf565b60405190808252806020026020018201604052801561102d578160200160208202803683370190505b5060075490915060009061104390600190613432565b905060006110518587613449565b61105b9083613432565b90506000805b838310156111d35761107387846134d5565b915060006007848154811061108a5761108a6134ed565b906000526020600020906003020160000154600784815481106110af576110af6134ed565b9060005260206000209060030201600001546110cb9190613432565b9050600081600786815481106110e3576110e36134ed565b90600052602060002090600302016001015460078681548110611108576111086134ed565b9060005260206000209060030201600101546111249190613432565b61112e9190613468565b905060008260078781548110611146576111466134ed565b9060005260206000209060030201600201546007878154811061116b5761116b6134ed565b9060005260206000209060030201600201546111879190613432565b6111919190613468565b905061119f8c8e84846127a7565b8885815181106111b1576111b16134ed565b60209081029190910101525050506001016111cc87846134d5565b9250611061565b509293505050505b949350505050565b600b54600c544260008080611201600854600954600a549192909190565b92509250925083811461124a57600061121a8286613432565b90506112268185613449565b61123090886134d5565b965061123c8184613449565b61124690876134d5565b9550505b505050909192565b6001600160a01b03831660008181526003602090815260408083203380855292528220549192909190821480159061128c57506000198114155b1561130257600061129d8583613432565b6001600160a01b038881166000818152600360209081526040808320948916808452948252918290208590559051848152939450919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505b61130d868686612a9c565b6001925050505b9392505050565b6007818154811061132b57600080fd5b600091825260209091206003909102018054600182015460029092015490925083565b6000806113596119c0565b90506000806113666111e3565b50845191935091504214156113cf576007805461138590600290613432565b81548110611395576113956134ed565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505092505b82516000906113de9042613432565b90506000818560200151856113f39190613432565b6113fd9190613468565b90506000828660400151856114129190613432565b61141c9190613468565b905061142a888a84846127a7565b9998505050505050505050565b60606111db8484846001610fe7565b600060135460011461145757600080fd5b60026013556008546009546040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156114c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114ed9190613403565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157b9190613403565b905060006115898584613432565b905060006115978584613432565b600254909150806115d5576103e86115b76115b28486613449565b612b5c565b6115c19190613432565b97506115d060006103e8612bcc565b61160a565b611607876115e38386613449565b6115ed9190613468565b876115f88486613449565b6116029190613468565b612c5f565b97505b600088116116405760405162461bcd60e51b8152602060048201526003602482015262494c4d60e81b604482015260640161087f565b61164a8989612bcc565b61165685858989612613565b604080518481526020810184905233917f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f910160405180910390a250506001601355509395945050505050565b6000806013546001146116b557600080fd5b60026013556008546009546040516370a0823160e01b81523060048201527f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000906000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561174b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176f9190613403565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156117b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117dd9190613403565b3060009081526004602052604090205460025491925090806117ff8584613449565b6118099190613468565b9950806118168484613449565b6118209190613468565b985060008a1180156118325750600089115b6118645760405162461bcd60e51b815260206004820152600360248201526224a62160e91b604482015260640161087f565b61186e3083612c75565b611879868c8c612245565b611884858c8b612245565b6040516370a0823160e01b81523060048201526001600160a01b038716906370a0823190602401602060405180830381865afa1580156118c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ec9190613403565b6040516370a0823160e01b81523060048201529094506001600160a01b038616906370a0823190602401602060405180830381865afa158015611933573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119579190613403565b925061196584848a8a612613565b604080518b8152602081018b90526001600160a01b038d169133917fdccd412f0b1252819cb1fd330b93224ca42612892bb3f4f789976e6d81936496910160405180910390a350505050505050506001601381905550915091565b6119e460405180606001604052806000815260200160008152602001600081525090565b600780546119f490600190613432565b81548110611a0457611a046134ed565b90600052602060002090600302016040518060600160405290816000820154815260200160018201548152602001600282015481525050905090565b60018054610ef99061348a565b600080611a5d8585856001610fe7565b90506000805b8251811015611aa557828181518110611a7e57611a7e6134ed565b602002602001015182611a9191906134d5565b915080611a9d81613503565b915050611a63565b50611ab08482613468565b9695505050505050565b6000611ac7338484612a9c565b50600192915050565b601354600114611adf57600080fd5b60026013556008546040516370a0823160e01b81523060048201527f0000000000000000000000000000000000000000000000000000000000000000917f000000000000000000000000000000000000000000000000000000000000000091611bac9184918691906001600160a01b038416906370a08231906024015b602060405180830381865afa158015611b79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b9d9190613403565b611ba79190613432565b612245565b6009546040516370a0823160e01b8152306004820152611be49183918691906001600160a01b038416906370a0823190602401611b5c565b5050600160135550565b600080611bfa33612d00565b50503360009081526011602090815260408083205460129092529091205481151580611c265750600081115b15611d11573360008181526011602090815260408083208390556012909152808220919091555163299e7ae760e11b8152600481019190915260248101839052604481018290526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063533cf5ce90606401600060405180830381600087803b158015611cbb57600080fd5b505af1158015611ccf573d6000803e3d6000fd5b505060408051858152602081018590523393508392507f865ca08d59f5cb456e85cd2f7ef63664ea4f73327414e9d8152c4158b0e94645910160405180910390a35b9091565b42841015611d555760405162461bcd60e51b815260206004820152600d60248201526c14185a5c8e8811561412549151609a1b604482015260640161087f565b7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051611d85919061351e565b60408051918290038220828201825260018352603160f81b6020938401528151928301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160408051601f19818403018152918152815160209283012060058190556001600160a01b038a166000908152600690935290822080547f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918b918b918b919087611e5083613503565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810187905260e00160405160208183030381529060405280519060200120604051602001611ec992919061190160f01b81526002810192909252602282015260420190565b60408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015611f34573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615801590611f6a5750886001600160a01b0316816001600160a01b0316145b611fb65760405162461bcd60e51b815260206004820152601760248201527f506169723a20494e56414c49445f5349474e4154555245000000000000000000604482015260640161087f565b6001600160a01b038981166000818152600360209081526040808320948d16808452948252918290208b905590518a81527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050505050505050565b600854600954604051632895a2f560e11b81527f0000000000000000000000000000000000000000000000000000000000000000151560048201526000929190612710907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063512b45ea90602401602060405180830381865afa1580156120b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120d69190613403565b6120e09087613449565b6120ea9190613468565b6120f49086613432565b9450612102858584846127a7565b95945050505050565b60135460011461211a57600080fd5b60026013556040516370a0823160e01b815230600482015261223e907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612187573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121ab9190613403565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561220f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122339190613403565b600854600954612613565b6001601355565b6000836001600160a01b03163b1161225c57600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916122b891906135ba565b6000604051808303816000865af19150503d80600081146122f5576040519150601f19603f3d011682016040523d82523d6000602084013e6122fa565b606091505b50915091508180156123245750805115806123245750808060200190518101906123249190613395565b61232d57600080fd5b5050505050565b61237f7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083612245565b60025460009061239783670de0b6b3a7640000613449565b6123a19190613468565b905080156123c15780600d60008282546123bb91906134d5565b90915550505b604080518381526000602082015233917f112c256902bf554b6ed882d2936687aaeb4225e8cd5b51303c90ca6cf43a860291015b60405180910390a25050565b61244c7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083612245565b60025460009061246483670de0b6b3a7640000613449565b61246e9190613468565b9050801561248e5780600e600082825461248891906134d5565b90915550505b60408051600081526020810184905233917f112c256902bf554b6ed882d2936687aaeb4225e8cd5b51303c90ca6cf43a860291016123f5565b60007f0000000000000000000000000000000000000000000000000000000000000000156126025760007f000000000000000000000000000000000000000000000000000000000000000061252485670de0b6b3a7640000613449565b61252e9190613468565b905060007f000000000000000000000000000000000000000000000000000000000000000061256585670de0b6b3a7640000613449565b61256f9190613468565b90506000670de0b6b3a76400006125868385613449565b6125909190613468565b90506000670de0b6b3a76400006125a78480613449565b6125b19190613468565b670de0b6b3a76400006125c48680613449565b6125ce9190613468565b6125d891906134d5565b9050670de0b6b3a76400006125ed8284613449565b6125f79190613468565b945050505050610fe1565b61260c8284613449565b9050610fe1565b600a5442906000906126259083613432565b905060008111801561263657508315155b801561264157508215155b15612688576126508185613449565b600b600082825461266191906134d5565b9091555061267190508184613449565b600c600082825461268291906134d5565b90915550505b60006126926119c0565b80519091506126a19084613432565b91506107088211156127565760408051606081018252848152600b5460208201908152600c549282019283526007805460018101825560009190915291517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688600390930292830155517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68982015590517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68a909101555b60088790556009869055600a83905560408051888152602081018890527fcf2aa50876cdfbb541206f89af0ee78d44a2abf8d328e37fa4917f982149848a910160405180910390a150505050505050565b60007f000000000000000000000000000000000000000000000000000000000000000015612a2a5760006127db84846124c7565b90507f000000000000000000000000000000000000000000000000000000000000000061281085670de0b6b3a7640000613449565b61281a9190613468565b93507f000000000000000000000000000000000000000000000000000000000000000061284f84670de0b6b3a7640000613449565b6128599190613468565b92506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b03161461289e5784866128a1565b85855b915091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b031614612920577f000000000000000000000000000000000000000000000000000000000000000061291189670de0b6b3a7640000613449565b61291b9190613468565b61295d565b7f000000000000000000000000000000000000000000000000000000000000000061295389670de0b6b3a7640000613449565b61295d9190613468565b9750600061297561296e848b6134d5565b8584612e60565b61297f9083613432565b9050670de0b6b3a76400007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316896001600160a01b0316146129e9577f0000000000000000000000000000000000000000000000000000000000000000612a0b565b7f00000000000000000000000000000000000000000000000000000000000000005b612a159083613449565b612a1f9190613468565b9450505050506111db565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316866001600160a01b031614612a6d578385612a70565b84845b9092509050612a7f87836134d5565b612a898289613449565b612a939190613468565b925050506111db565b612aa583612d00565b612aae82612d00565b6001600160a01b03831660009081526004602052604081208054839290612ad6908490613432565b90915550506001600160a01b03821660009081526004602052604081208054839290612b039084906134d5565b92505081905550816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051612b4f91815260200190565b60405180910390a3505050565b60006003821115612bbd5750806000612b76600283613468565b612b819060016134d5565b90505b81811015612bb757905080600281612b9c8186613468565b612ba691906134d5565b612bb09190613468565b9050612b84565b50919050565b8115612bc7575060015b919050565b612bd582612d00565b8060026000828254612be791906134d5565b90915550506001600160a01b03821660009081526004602052604081208054839290612c149084906134d5565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b6000818310612c6e5781611314565b5090919050565b612c7e82612d00565b8060026000828254612c909190613432565b90915550506001600160a01b03821660009081526004602052604081208054839290612cbd908490613432565b90915550506040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001612c53565b6001600160a01b0381166000908152600460205260409020548015612e2e576001600160a01b0382166000908152600f60209081526040808320805460108085529285208054600d54600e54948190559490955282905593612d628584613432565b90506000612d708584613432565b90508115612dcb576000670de0b6b3a7640000612d8d848a613449565b612d979190613468565b6001600160a01b038a16600090815260116020526040812080549293508392909190612dc49084906134d5565b9091555050505b8015612e24576000670de0b6b3a7640000612de6838a613449565b612df09190613468565b6001600160a01b038a16600090815260126020526040812080549293508392909190612e1d9084906134d5565b9091555050505b5050505050505050565b600d546001600160a01b0383166000908152600f6020908152604080832093909355600e546010909152919020555050565b6000805b60ff811015612f6657826000612e7a8783612f6f565b905085811015612eca576000612e90888761300c565b612e9a8389613432565b612eac90670de0b6b3a7640000613449565b612eb69190613468565b9050612ec281876134d5565b955050612f0c565b6000612ed6888761300c565b612ee08884613432565b612ef290670de0b6b3a7640000613449565b612efc9190613468565b9050612f088187613432565b9550505b81851115612f35576001612f208387613432565b11612f3057849350505050611314565b612f51565b6001612f418684613432565b11612f5157849350505050611314565b50508080612f5e90613503565b915050612e64565b50909392505050565b6000670de0b6b3a764000082818581612f888280613449565b612f929190613468565b612f9c9190613449565b612fa69190613468565b612fb09190613449565b612fba9190613468565b670de0b6b3a7640000808481612fd08280613449565b612fda9190613468565b612fe49190613449565b612fee9190613468565b612ff89086613449565b6130029190613468565b61131491906134d5565b6000670de0b6b3a764000083816130238280613449565b61302d9190613468565b6130379190613449565b6130419190613468565b670de0b6b3a7640000806130558580613449565b61305f9190613468565b61306a866003613449565b612ff89190613449565b80356001600160a01b0381168114612bc757600080fd5b6000806000806000608086880312156130a357600080fd5b85359450602086013593506130ba60408701613074565b9250606086013567ffffffffffffffff808211156130d757600080fd5b818801915088601f8301126130eb57600080fd5b8135818111156130fa57600080fd5b89602082850101111561310c57600080fd5b9699959850939650602001949392505050565b60005b8381101561313a578181015183820152602001613122565b83811115613149576000848401525b50505050565b602081526000825180602084015261316e81604085016020870161311f565b601f01601f19169190910160400192915050565b6000806040838503121561319557600080fd5b61319e83613074565b946020939093013593505050565b600080600080608085870312156131c257600080fd5b6131cb85613074565b966020860135965060408601359560600135945092505050565b6020808252825182820181905260009190848201906040850190845b8181101561321d57835183529284019291840191600101613201565b50909695505050505050565b60006020828403121561323b57600080fd5b61131482613074565b60008060006060848603121561325957600080fd5b61326284613074565b925061327060208501613074565b9150604084013590509250925092565b60006020828403121561329257600080fd5b5035919050565b6000806000606084860312156132ae57600080fd5b6132b784613074565b95602085013595506040909401359392505050565b600080600080600080600060e0888a0312156132e757600080fd5b6132f088613074565b96506132fe60208901613074565b95506040880135945060608801359350608088013560ff8116811461332257600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561335257600080fd5b61335b83613074565b915061336960208401613074565b90509250929050565b6000806040838503121561338557600080fd5b8235915061336960208401613074565b6000602082840312156133a757600080fd5b8151801515811461131457600080fd5b60018060a01b038616815284602082015283604082015260806060820152816080820152818360a0830137600081830160a090810191909152601f909201601f19160101949350505050565b60006020828403121561341557600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b6000828210156134445761344461341c565b500390565b60008160001904831182151516156134635761346361341c565b500290565b60008261348557634e487b7160e01b600052601260045260246000fd5b500490565b600181811c9082168061349e57607f821691505b60208210811415612bb757634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b600082198211156134e8576134e861341c565b500190565b634e487b7160e01b600052603260045260246000fd5b60006000198214156135175761351761341c565b5060010190565b600080835481600182811c91508083168061353a57607f831692505b602080841082141561355a57634e487b7160e01b86526022600452602486fd5b81801561356e576001811461357f576135ac565b60ff198616895284890196506135ac565b60008a81526020902060005b868110156135a45781548b82015290850190830161358b565b505084890196505b509498975050505050505050565b600082516135cc81846020870161311f565b919091019291505056fea264697066735822122012bbc26e6d059c64971894076e1c061b5af33828e3ac3045418303c8201d1fe764736f6c634300080b003360e060405234801561001057600080fd5b5060405161037038038061037083398101604081905261002f91610066565b336080526001600160a01b0391821660a0521660c052610099565b80516001600160a01b038116811461006157600080fd5b919050565b6000806040838503121561007957600080fd5b6100828361004a565b91506100906020840161004a565b90509250929050565b60805160a05160c0516102ab6100c5600039600060b601526000608501526000605001526102ab6000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063533cf5ce14610030575b600080fd5b61004361003e3660046101d0565b610045565b005b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461007a57600080fd5b81156100ab576100ab7f000000000000000000000000000000000000000000000000000000000000000084846100e1565b80156100dc576100dc7f000000000000000000000000000000000000000000000000000000000000000084836100e1565b505050565b6000836001600160a01b03163b116100f857600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916101549190610211565b6000604051808303816000865af19150503d8060008114610191576040519150601f19603f3d011682016040523d82523d6000602084013e610196565b606091505b50915091508180156101c05750805115806101c05750808060200190518101906101c0919061024c565b6101c957600080fd5b5050505050565b6000806000606084860312156101e557600080fd5b83356001600160a01b03811681146101fc57600080fd5b95602085013595506040909401359392505050565b6000825160005b818110156102325760208186018101518583015201610218565b81811115610241576000828501525b509190910192915050565b60006020828403121561025e57600080fd5b8151801515811461026e57600080fd5b939250505056fea26469706673582212205f5c908ef3326a953bd870e8659b0596ff46fae086e9667d4d69778578e14adc64736f6c634300080b0033a2646970667358221220d044256e297a575c8be24751aa6c5b37af7db2fd6b3617dfa590cf6bacfad43964736f6c634300080b0033", + "deployedBytecode": "0x60806040523480156200001157600080fd5b50600436106200015d5760003560e01c80639a7165e411620000c7578063bedb86fb1162000086578063bedb86fb14620002da578063d0fb020314620002f1578063e1f76b441462000305578063e5e31b13146200031c578063eb13c4cf1462000342578063f94c53c7146200037b57600080fd5b80639a7165e4146200027b5780639aab9248146200028f5780639fd0506d1462000299578063b187bd2614620002b2578063bc063e1a14620002d157600080fd5b80635084ed0311620001205780635084ed0314620001e9578063512b45ea14620001f3578063574f2ba3146200020a5780636801cc30146200021357806382dfdce414620002505780638a4fa0d2146200026757600080fd5b8063167a6f9014620001625780631e3dd18b146200016e5780632d88af4a14620001a257806340bbd77514620001b9578063472d35b914620001d2575b600080fd5b6200016c62000385565b005b620001856200017f36600462000987565b620003c8565b6040516001600160a01b0390911681526020015b60405180910390f35b6200016c620001b3366004620009be565b620003f3565b620001c360025481565b60405190815260200162000199565b6200016c620001e3366004620009be565b62000432565b620001c360035481565b620001c362000204366004620009f4565b620004a6565b600754620001c3565b620001856200022436600462000a12565b60066020908152600093845260408085208252928452828420905282529020546001600160a01b031681565b620001856200026136600462000a12565b620004c1565b60055462000185906001600160a01b031681565b60015462000185906001600160a01b031681565b620001c3620007ad565b600054620001859061010090046001600160a01b031681565b600054620002c09060ff1681565b604051901515815260200162000199565b620001c3600581565b6200016c620002eb366004620009f4565b620007e1565b60045462000185906001600160a01b031681565b6200016c6200031636600462000a5c565b62000811565b620002c06200032d366004620009be565b60086020526000908152604090205460ff1681565b600954600a54604080516001600160a01b0393841681529282166020840152600160a01b90910460ff1615159082015260600162000199565b6200016c620008f9565b6001546001600160a01b031633146200039d57600080fd5b600154600080546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b60078181548110620003d957600080fd5b6000918252602090912001546001600160a01b0316905081565b60005461010090046001600160a01b031633146200041057600080fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6004546001600160a01b03163314620004845760405162461bcd60e51b815260206004820152600f60248201526e3737ba103332b29036b0b730b3b2b960891b60448201526064015b60405180910390fd5b600580546001600160a01b0319166001600160a01b0392909216919091179055565b600081620004b757600354620004bb565b6002545b92915050565b6000826001600160a01b0316846001600160a01b031614156200050c5760405162461bcd60e51b8152602060048201526002602482015261494160f01b60448201526064016200047b565b600080846001600160a01b0316866001600160a01b0316106200053157848662000534565b85855b90925090506001600160a01b038216620005765760405162461bcd60e51b81526020600482015260026024820152615a4160f01b60448201526064016200047b565b6001600160a01b0382811660009081526006602090815260408083208585168452825280832088151584529091529020541615620005dc5760405162461bcd60e51b8152602060048201526002602482015261504560f01b60448201526064016200047b565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b16603482015284151560f81b604882015260009060490160408051601f19818403018152908290528051602090910120600a80546001600160a01b038087166001600160a01b03198b1515600160a01b0281166001600160a81b03199094169390931717909255600980549288169290911691909117905591508190620006839062000979565b8190604051809103906000f5905080158015620006a4573d6000803e3d6000fd5b506001600160a01b0384811660008181526006602081815260408084208987168086529083528185208d15158087529084528286208054988a166001600160a01b0319998a16811790915582875294845282862087875284528286208187528452828620805489168617905560078054600181810183557fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6889091018054909a1687179099558587526008855295839020805460ff1916909817909755935481519687529186019290925290840152929650907fc4805696c66d7cf352fc1d6bb633ad5ee82f6cb577c453024b6e0eb8306c6fc99060600160405180910390a35050509392505050565b600060405180602001620007c19062000979565b6020820181038252601f19601f8201166040525080519060200120905090565b60005461010090046001600160a01b03163314620007fe57600080fd5b6000805460ff1916911515919091179055565b6004546001600160a01b031633146200085f5760405162461bcd60e51b815260206004820152600f60248201526e3737ba103332b29036b0b730b3b2b960891b60448201526064016200047b565b6005811115620008a15760405162461bcd60e51b815260206004820152600c60248201526b0cccaca40e8dede40d0d2ced60a31b60448201526064016200047b565b80620008e65760405162461bcd60e51b8152602060048201526013602482015272666565206d757374206265206e6f6e7a65726f60681b60448201526064016200047b565b8115620008f35760025550565b60035550565b6005546001600160a01b03163314620009555760405162461bcd60e51b815260206004820152601760248201527f6e6f742070656e64696e6720666565206d616e6167657200000000000000000060448201526064016200047b565b600554600480546001600160a01b0319166001600160a01b03909216919091179055565b6146d28062000a8a83390190565b6000602082840312156200099a57600080fd5b5035919050565b80356001600160a01b0381168114620009b957600080fd5b919050565b600060208284031215620009d157600080fd5b620009dc82620009a1565b9392505050565b80358015158114620009b957600080fd5b60006020828403121562000a0757600080fd5b620009dc82620009e3565b60008060006060848603121562000a2857600080fd5b62000a3384620009a1565b925062000a4360208501620009a1565b915062000a5360408501620009e3565b90509250925092565b6000806040838503121562000a7057600080fd5b62000a7b83620009e3565b94602093909301359350505056fe61016060405260006002556000600d556000600e5560016013553480156200002657600080fd5b50336001600160a01b0316610100816001600160a01b0316815250506000806000336001600160a01b031663eb13c4cf6040518163ffffffff1660e01b8152600401606060405180830381865afa15801562000086573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ac9190620007c8565b8015156080526001600160a01b0380831660c052831660a052604051929550909350915083908390620000df90620006f7565b6001600160a01b03928316815291166020820152604001604051809103906000f08015801562000113573d6000803e3d6000fd5b506001600160a01b031660e05280156200034557826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000166573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000190919081019062000863565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620001cf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620001f9919081019062000863565b6040516020016200020c9291906200091b565b604051602081830303815290604052600090805190602001906200023292919062000705565b50826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000272573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526200029c919081019062000863565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620002db573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000305919081019062000863565b6040516020016200031892919062000976565b604051602081830303815290604052600190805190602001906200033e92919062000705565b506200055e565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000384573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620003ae919081019062000863565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620003ed573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000417919081019062000863565b6040516020016200042a929190620009c7565b604051602081830303815290604052600090805190602001906200045092919062000705565b50826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000490573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620004ba919081019062000863565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620004f9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000523919081019062000863565b6040516020016200053692919062000a24565b604051602081830303815290604052600190805190602001906200055c92919062000705565b505b826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200059d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620005c3919062000a46565b620005d090600a62000b87565b6101208181525050816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000617573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200063d919062000a46565b6200064a90600a62000b87565b6101405250506040805160608101825242815260006020820181815292820181815260078054600181018255925291517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68860039092029182015591517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c689830155517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68a909101555062000bd5565b610370806200436283390190565b828054620007139062000b98565b90600052602060002090601f01602090048101928262000737576000855562000782565b82601f106200075257805160ff191683800117855562000782565b8280016001018555821562000782579182015b828111156200078257825182559160200191906001019062000765565b506200079092915062000794565b5090565b5b8082111562000790576000815560010162000795565b80516001600160a01b0381168114620007c357600080fd5b919050565b600080600060608486031215620007de57600080fd5b620007e984620007ab565b9250620007f960208501620007ab565b9150604084015180151581146200080f57600080fd5b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b60005b838110156200084d57818101518382015260200162000833565b838111156200085d576000848401525b50505050565b6000602082840312156200087657600080fd5b81516001600160401b03808211156200088e57600080fd5b818401915084601f830112620008a357600080fd5b815181811115620008b857620008b86200081a565b604051601f8201601f19908116603f01168101908382118183101715620008e357620008e36200081a565b81604052828152876020848701011115620008fd57600080fd5b6200091083602083016020880162000830565b979650505050505050565b6e029ba30b13632ab189020a6a690169608d1b8152600083516200094781600f85016020880162000830565b602f60f81b600f9184019182015283516200096a81601084016020880162000830565b01601001949350505050565b6473414d4d2d60d81b8152600083516200099881600585016020880162000830565b602f60f81b6005918401918201528351620009bb81600684016020880162000830565b01600601949350505050565b7002b37b630ba34b632ab189020a6a690169607d1b815260008351620009f581601185016020880162000830565b602f60f81b601191840191820152835162000a1881601284016020880162000830565b01601201949350505050565b6476414d4d2d60d81b8152600083516200099881600585016020880162000830565b60006020828403121562000a5957600080fd5b815160ff8116811462000a6b57600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b600181815b8085111562000ac957816000190482111562000aad5762000aad62000a72565b8085161562000abb57918102915b93841c939080029062000a8d565b509250929050565b60008262000ae25750600162000b81565b8162000af15750600062000b81565b816001811462000b0a576002811462000b155762000b35565b600191505062000b81565b60ff84111562000b295762000b2962000a72565b50506001821b62000b81565b5060208310610133831016604e8410600b841016171562000b5a575081810a62000b81565b62000b66838362000a88565b806000190482111562000b7d5762000b7d62000a72565b0290505b92915050565b600062000a6b60ff84168362000ad1565b600181811c9082168062000bad57607f821691505b6020821081141562000bcf57634e487b7160e01b600052602260045260246000fd5b50919050565b60805160a05160c05160e05161010051610120516101405161360c62000d5660003960008181610431015281816125340152818161281e015281816128e001526129eb01526000818161040e015281816124f3015281816127df0152818161292201526129c50152600081816107b801528181610c0701528181610cd801526120630152600081816105f201528181611c770152818161235a01526124270152600081816104bb0152818161064701528181610714015281816108f301528181610b9b01528181611510015281816116f701528181611b1e015281816121c001526124060152600081816102f90152818161049301528181610622015281816108d201528181610b7a0152818161147a015281816116d501528181611afc015281816121380152818161233901528181612860015281816128a70152818161298c0152612a2f0152600081816103970152818161046301528181610bcf01528181610ca001528181612032015281816124cb01526127ab015261360c6000f3fe608060405234801561001057600080fd5b50600436106102745760003560e01c80637ecebe0011610151578063bda39cad116100c3578063d294f09311610087578063d294f09314610736578063d505accf1461073e578063dd62ed3e14610751578063ebeb31db1461077c578063f140a35a14610784578063fff6cae91461079757600080fd5b8063bda39cad146106eb578063bf944dbc146106f4578063c245febc146106fd578063c5700a0214610706578063d21220a71461070f57600080fd5b80639d63848a116101155780639d63848a146106145780639e8cc04b146106725780639f767c8814610685578063a1ac4d13146106a5578063a9059cbb146106c5578063bc25cf77146106d857600080fd5b80637ecebe001461057357806389afcb44146105935780638a7b8cf2146105bb57806395d89b41146105e55780639af1d35a146105ed57600080fd5b8063252c09d7116101ea5780634d5a9f8a116101ae5780634d5a9f8a146104f1578063517b3f82146105115780635881c475146105245780635a76f25e146105375780636a6278421461054057806370a082311461055357600080fd5b8063252c09d7146103cc578063313ce567146103df57806332c0defd146103f9578063392f37e914610402578063443cb4bc146104e857600080fd5b806313345fe11161023c57806313345fe11461033357806318160ddd146103535780631df8c7171461036a578063205aabf11461037257806322be3de11461039257806323b872dd146103b957600080fd5b8063022c0d9f1461027957806306fdde031461028e5780630902f1ac146102ac578063095ea7b3146102d15780630dfe1681146102f4575b600080fd5b61028c61028736600461308b565b61079f565b005b610296610eec565b6040516102a3919061314f565b60405180910390f35b600854600954600a545b604080519384526020840192909252908201526060016102a3565b6102e46102df366004613182565b610f7a565b60405190151581526020016102a3565b61031b7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016102a3565b6103466103413660046131ac565b610fe7565b6040516102a391906131e5565b61035c60025481565b6040519081526020016102a3565b6102b66111e3565b61035c610380366004613229565b60106020526000908152604090205481565b6102e47f000000000000000000000000000000000000000000000000000000000000000081565b6102e46103c7366004613244565b611252565b6102b66103da366004613280565b61131b565b6103e7601281565b60405160ff90911681526020016102a3565b61035c600d5481565b600854600954604080517f000000000000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060208201529081019290925260608201527f0000000000000000000000000000000000000000000000000000000000000000151560808201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660a08301527f00000000000000000000000000000000000000000000000000000000000000001660c082015260e0016102a3565b61035c60085481565b61035c6104ff366004613229565b60116020526000908152604090205481565b61035c61051f366004613182565b61134e565b610346610532366004613299565b611437565b61035c60095481565b61035c61054e366004613229565b611446565b61035c610561366004613229565b60046020526000908152604090205481565b61035c610581366004613229565b60066020526000908152604090205481565b6105a66105a1366004613229565b6116a3565b604080519283526020830191909152016102a3565b6105c36119c0565b60408051825181526020808401519082015291810151908201526060016102a3565b610296611a40565b61031b7f000000000000000000000000000000000000000000000000000000000000000081565b604080516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000166020820152016102a3565b61035c610680366004613299565b611a4d565b61035c610693366004613229565b600f6020526000908152604090205481565b61035c6106b3366004613229565b60126020526000908152604090205481565b6102e46106d3366004613182565b611aba565b61028c6106e6366004613229565b611ad0565b61035c600e5481565b61035c600b5481565b61035c600c5481565b61035c600a5481565b61031b7f000000000000000000000000000000000000000000000000000000000000000081565b6105a6611bee565b61028c61074c3660046132cc565b611d15565b61035c61075f36600461333f565b600360209081526000928352604080842090915290825290205481565b60075461035c565b61035c610792366004613372565b61201d565b61028c61210b565b6013546001146107ae57600080fd5b60026013819055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b187bd266040518163ffffffff1660e01b8152600401602060405180830381865afa158015610814573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108389190613395565b1561084257600080fd5b60008511806108515750600084115b6108885760405162461bcd60e51b8152602060048201526003602482015262494f4160e81b60448201526064015b60405180910390fd5b600854600954818710801561089c57508086105b6108cd5760405162461bcd60e51b8152602060048201526002602482015261125360f21b604482015260640161087f565b6000807f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03898116908316148015906109405750806001600160a01b0316896001600160a01b031614155b6109715760405162461bcd60e51b8152602060048201526002602482015261125560f21b604482015260640161087f565b8a1561098257610982828a8d612245565b891561099357610993818a8c612245565b8615610a0057604051639a7bff7960e01b81526001600160a01b038a1690639a7bff79906109cd9033908f908f908e908e906004016133b7565b600060405180830381600087803b1580156109e757600080fd5b505af11580156109fb573d6000803e3d6000fd5b505050505b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015610a44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a689190613403565b6040516370a0823160e01b81523060048201529094506001600160a01b038216906370a0823190602401602060405180830381865afa158015610aaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad39190613403565b9250505060008985610ae59190613432565b8311610af2576000610b06565b610afc8a86613432565b610b069084613432565b90506000610b148a86613432565b8311610b21576000610b35565b610b2b8a86613432565b610b359084613432565b90506000821180610b465750600081115b610b785760405162461bcd60e51b815260206004820152600360248201526249494160e81b604482015260640161087f565b7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008315610c8b57604051632895a2f560e11b81527f000000000000000000000000000000000000000000000000000000000000000015156004820152610c8b90612710906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063512b45ea90602401602060405180830381865afa158015610c4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c729190613403565b610c7c9087613449565b610c869190613468565b612334565b8215610d5c57604051632895a2f560e11b81527f000000000000000000000000000000000000000000000000000000000000000015156004820152610d5c90612710906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063512b45ea90602401602060405180830381865afa158015610d1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d439190613403565b610d4d9086613449565b610d579190613468565b612401565b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015610da0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc49190613403565b6040516370a0823160e01b81523060048201529096506001600160a01b038216906370a0823190602401602060405180830381865afa158015610e0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2f9190613403565b9450610e3b88886124c7565b610e4587876124c7565b1015610e775760405162461bcd60e51b81526020600482015260016024820152604b60f81b604482015260640161087f565b5050610e8584848888612613565b60408051838152602081018390529081018c9052606081018b90526001600160a01b038a169033907fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d8229060800160405180910390a350506001601355505050505050505050565b60008054610ef99061348a565b80601f0160208091040260200160405190810160405280929190818152602001828054610f259061348a565b8015610f725780601f10610f4757610100808354040283529160200191610f72565b820191906000526020600020905b815481529060010190602001808311610f5557829003601f168201915b505050505081565b3360008181526003602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610fd59086815260200190565b60405180910390a35060015b92915050565b606060008367ffffffffffffffff811115611004576110046134bf565b60405190808252806020026020018201604052801561102d578160200160208202803683370190505b5060075490915060009061104390600190613432565b905060006110518587613449565b61105b9083613432565b90506000805b838310156111d35761107387846134d5565b915060006007848154811061108a5761108a6134ed565b906000526020600020906003020160000154600784815481106110af576110af6134ed565b9060005260206000209060030201600001546110cb9190613432565b9050600081600786815481106110e3576110e36134ed565b90600052602060002090600302016001015460078681548110611108576111086134ed565b9060005260206000209060030201600101546111249190613432565b61112e9190613468565b905060008260078781548110611146576111466134ed565b9060005260206000209060030201600201546007878154811061116b5761116b6134ed565b9060005260206000209060030201600201546111879190613432565b6111919190613468565b905061119f8c8e84846127a7565b8885815181106111b1576111b16134ed565b60209081029190910101525050506001016111cc87846134d5565b9250611061565b509293505050505b949350505050565b600b54600c544260008080611201600854600954600a549192909190565b92509250925083811461124a57600061121a8286613432565b90506112268185613449565b61123090886134d5565b965061123c8184613449565b61124690876134d5565b9550505b505050909192565b6001600160a01b03831660008181526003602090815260408083203380855292528220549192909190821480159061128c57506000198114155b1561130257600061129d8583613432565b6001600160a01b038881166000818152600360209081526040808320948916808452948252918290208590559051848152939450919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505b61130d868686612a9c565b6001925050505b9392505050565b6007818154811061132b57600080fd5b600091825260209091206003909102018054600182015460029092015490925083565b6000806113596119c0565b90506000806113666111e3565b50845191935091504214156113cf576007805461138590600290613432565b81548110611395576113956134ed565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505092505b82516000906113de9042613432565b90506000818560200151856113f39190613432565b6113fd9190613468565b90506000828660400151856114129190613432565b61141c9190613468565b905061142a888a84846127a7565b9998505050505050505050565b60606111db8484846001610fe7565b600060135460011461145757600080fd5b60026013556008546009546040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156114c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114ed9190613403565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157b9190613403565b905060006115898584613432565b905060006115978584613432565b600254909150806115d5576103e86115b76115b28486613449565b612b5c565b6115c19190613432565b97506115d060006103e8612bcc565b61160a565b611607876115e38386613449565b6115ed9190613468565b876115f88486613449565b6116029190613468565b612c5f565b97505b600088116116405760405162461bcd60e51b8152602060048201526003602482015262494c4d60e81b604482015260640161087f565b61164a8989612bcc565b61165685858989612613565b604080518481526020810184905233917f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f910160405180910390a250506001601355509395945050505050565b6000806013546001146116b557600080fd5b60026013556008546009546040516370a0823160e01b81523060048201527f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000906000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561174b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176f9190613403565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156117b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117dd9190613403565b3060009081526004602052604090205460025491925090806117ff8584613449565b6118099190613468565b9950806118168484613449565b6118209190613468565b985060008a1180156118325750600089115b6118645760405162461bcd60e51b815260206004820152600360248201526224a62160e91b604482015260640161087f565b61186e3083612c75565b611879868c8c612245565b611884858c8b612245565b6040516370a0823160e01b81523060048201526001600160a01b038716906370a0823190602401602060405180830381865afa1580156118c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ec9190613403565b6040516370a0823160e01b81523060048201529094506001600160a01b038616906370a0823190602401602060405180830381865afa158015611933573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119579190613403565b925061196584848a8a612613565b604080518b8152602081018b90526001600160a01b038d169133917fdccd412f0b1252819cb1fd330b93224ca42612892bb3f4f789976e6d81936496910160405180910390a350505050505050506001601381905550915091565b6119e460405180606001604052806000815260200160008152602001600081525090565b600780546119f490600190613432565b81548110611a0457611a046134ed565b90600052602060002090600302016040518060600160405290816000820154815260200160018201548152602001600282015481525050905090565b60018054610ef99061348a565b600080611a5d8585856001610fe7565b90506000805b8251811015611aa557828181518110611a7e57611a7e6134ed565b602002602001015182611a9191906134d5565b915080611a9d81613503565b915050611a63565b50611ab08482613468565b9695505050505050565b6000611ac7338484612a9c565b50600192915050565b601354600114611adf57600080fd5b60026013556008546040516370a0823160e01b81523060048201527f0000000000000000000000000000000000000000000000000000000000000000917f000000000000000000000000000000000000000000000000000000000000000091611bac9184918691906001600160a01b038416906370a08231906024015b602060405180830381865afa158015611b79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b9d9190613403565b611ba79190613432565b612245565b6009546040516370a0823160e01b8152306004820152611be49183918691906001600160a01b038416906370a0823190602401611b5c565b5050600160135550565b600080611bfa33612d00565b50503360009081526011602090815260408083205460129092529091205481151580611c265750600081115b15611d11573360008181526011602090815260408083208390556012909152808220919091555163299e7ae760e11b8152600481019190915260248101839052604481018290526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063533cf5ce90606401600060405180830381600087803b158015611cbb57600080fd5b505af1158015611ccf573d6000803e3d6000fd5b505060408051858152602081018590523393508392507f865ca08d59f5cb456e85cd2f7ef63664ea4f73327414e9d8152c4158b0e94645910160405180910390a35b9091565b42841015611d555760405162461bcd60e51b815260206004820152600d60248201526c14185a5c8e8811561412549151609a1b604482015260640161087f565b7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051611d85919061351e565b60408051918290038220828201825260018352603160f81b6020938401528151928301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160408051601f19818403018152918152815160209283012060058190556001600160a01b038a166000908152600690935290822080547f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918b918b918b919087611e5083613503565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810187905260e00160405160208183030381529060405280519060200120604051602001611ec992919061190160f01b81526002810192909252602282015260420190565b60408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015611f34573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615801590611f6a5750886001600160a01b0316816001600160a01b0316145b611fb65760405162461bcd60e51b815260206004820152601760248201527f506169723a20494e56414c49445f5349474e4154555245000000000000000000604482015260640161087f565b6001600160a01b038981166000818152600360209081526040808320948d16808452948252918290208b905590518a81527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050505050505050565b600854600954604051632895a2f560e11b81527f0000000000000000000000000000000000000000000000000000000000000000151560048201526000929190612710907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063512b45ea90602401602060405180830381865afa1580156120b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120d69190613403565b6120e09087613449565b6120ea9190613468565b6120f49086613432565b9450612102858584846127a7565b95945050505050565b60135460011461211a57600080fd5b60026013556040516370a0823160e01b815230600482015261223e907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612187573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121ab9190613403565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561220f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122339190613403565b600854600954612613565b6001601355565b6000836001600160a01b03163b1161225c57600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916122b891906135ba565b6000604051808303816000865af19150503d80600081146122f5576040519150601f19603f3d011682016040523d82523d6000602084013e6122fa565b606091505b50915091508180156123245750805115806123245750808060200190518101906123249190613395565b61232d57600080fd5b5050505050565b61237f7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083612245565b60025460009061239783670de0b6b3a7640000613449565b6123a19190613468565b905080156123c15780600d60008282546123bb91906134d5565b90915550505b604080518381526000602082015233917f112c256902bf554b6ed882d2936687aaeb4225e8cd5b51303c90ca6cf43a860291015b60405180910390a25050565b61244c7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083612245565b60025460009061246483670de0b6b3a7640000613449565b61246e9190613468565b9050801561248e5780600e600082825461248891906134d5565b90915550505b60408051600081526020810184905233917f112c256902bf554b6ed882d2936687aaeb4225e8cd5b51303c90ca6cf43a860291016123f5565b60007f0000000000000000000000000000000000000000000000000000000000000000156126025760007f000000000000000000000000000000000000000000000000000000000000000061252485670de0b6b3a7640000613449565b61252e9190613468565b905060007f000000000000000000000000000000000000000000000000000000000000000061256585670de0b6b3a7640000613449565b61256f9190613468565b90506000670de0b6b3a76400006125868385613449565b6125909190613468565b90506000670de0b6b3a76400006125a78480613449565b6125b19190613468565b670de0b6b3a76400006125c48680613449565b6125ce9190613468565b6125d891906134d5565b9050670de0b6b3a76400006125ed8284613449565b6125f79190613468565b945050505050610fe1565b61260c8284613449565b9050610fe1565b600a5442906000906126259083613432565b905060008111801561263657508315155b801561264157508215155b15612688576126508185613449565b600b600082825461266191906134d5565b9091555061267190508184613449565b600c600082825461268291906134d5565b90915550505b60006126926119c0565b80519091506126a19084613432565b91506107088211156127565760408051606081018252848152600b5460208201908152600c549282019283526007805460018101825560009190915291517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688600390930292830155517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68982015590517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68a909101555b60088790556009869055600a83905560408051888152602081018890527fcf2aa50876cdfbb541206f89af0ee78d44a2abf8d328e37fa4917f982149848a910160405180910390a150505050505050565b60007f000000000000000000000000000000000000000000000000000000000000000015612a2a5760006127db84846124c7565b90507f000000000000000000000000000000000000000000000000000000000000000061281085670de0b6b3a7640000613449565b61281a9190613468565b93507f000000000000000000000000000000000000000000000000000000000000000061284f84670de0b6b3a7640000613449565b6128599190613468565b92506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b03161461289e5784866128a1565b85855b915091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b031614612920577f000000000000000000000000000000000000000000000000000000000000000061291189670de0b6b3a7640000613449565b61291b9190613468565b61295d565b7f000000000000000000000000000000000000000000000000000000000000000061295389670de0b6b3a7640000613449565b61295d9190613468565b9750600061297561296e848b6134d5565b8584612e60565b61297f9083613432565b9050670de0b6b3a76400007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316896001600160a01b0316146129e9577f0000000000000000000000000000000000000000000000000000000000000000612a0b565b7f00000000000000000000000000000000000000000000000000000000000000005b612a159083613449565b612a1f9190613468565b9450505050506111db565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316866001600160a01b031614612a6d578385612a70565b84845b9092509050612a7f87836134d5565b612a898289613449565b612a939190613468565b925050506111db565b612aa583612d00565b612aae82612d00565b6001600160a01b03831660009081526004602052604081208054839290612ad6908490613432565b90915550506001600160a01b03821660009081526004602052604081208054839290612b039084906134d5565b92505081905550816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051612b4f91815260200190565b60405180910390a3505050565b60006003821115612bbd5750806000612b76600283613468565b612b819060016134d5565b90505b81811015612bb757905080600281612b9c8186613468565b612ba691906134d5565b612bb09190613468565b9050612b84565b50919050565b8115612bc7575060015b919050565b612bd582612d00565b8060026000828254612be791906134d5565b90915550506001600160a01b03821660009081526004602052604081208054839290612c149084906134d5565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b6000818310612c6e5781611314565b5090919050565b612c7e82612d00565b8060026000828254612c909190613432565b90915550506001600160a01b03821660009081526004602052604081208054839290612cbd908490613432565b90915550506040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001612c53565b6001600160a01b0381166000908152600460205260409020548015612e2e576001600160a01b0382166000908152600f60209081526040808320805460108085529285208054600d54600e54948190559490955282905593612d628584613432565b90506000612d708584613432565b90508115612dcb576000670de0b6b3a7640000612d8d848a613449565b612d979190613468565b6001600160a01b038a16600090815260116020526040812080549293508392909190612dc49084906134d5565b9091555050505b8015612e24576000670de0b6b3a7640000612de6838a613449565b612df09190613468565b6001600160a01b038a16600090815260126020526040812080549293508392909190612e1d9084906134d5565b9091555050505b5050505050505050565b600d546001600160a01b0383166000908152600f6020908152604080832093909355600e546010909152919020555050565b6000805b60ff811015612f6657826000612e7a8783612f6f565b905085811015612eca576000612e90888761300c565b612e9a8389613432565b612eac90670de0b6b3a7640000613449565b612eb69190613468565b9050612ec281876134d5565b955050612f0c565b6000612ed6888761300c565b612ee08884613432565b612ef290670de0b6b3a7640000613449565b612efc9190613468565b9050612f088187613432565b9550505b81851115612f35576001612f208387613432565b11612f3057849350505050611314565b612f51565b6001612f418684613432565b11612f5157849350505050611314565b50508080612f5e90613503565b915050612e64565b50909392505050565b6000670de0b6b3a764000082818581612f888280613449565b612f929190613468565b612f9c9190613449565b612fa69190613468565b612fb09190613449565b612fba9190613468565b670de0b6b3a7640000808481612fd08280613449565b612fda9190613468565b612fe49190613449565b612fee9190613468565b612ff89086613449565b6130029190613468565b61131491906134d5565b6000670de0b6b3a764000083816130238280613449565b61302d9190613468565b6130379190613449565b6130419190613468565b670de0b6b3a7640000806130558580613449565b61305f9190613468565b61306a866003613449565b612ff89190613449565b80356001600160a01b0381168114612bc757600080fd5b6000806000806000608086880312156130a357600080fd5b85359450602086013593506130ba60408701613074565b9250606086013567ffffffffffffffff808211156130d757600080fd5b818801915088601f8301126130eb57600080fd5b8135818111156130fa57600080fd5b89602082850101111561310c57600080fd5b9699959850939650602001949392505050565b60005b8381101561313a578181015183820152602001613122565b83811115613149576000848401525b50505050565b602081526000825180602084015261316e81604085016020870161311f565b601f01601f19169190910160400192915050565b6000806040838503121561319557600080fd5b61319e83613074565b946020939093013593505050565b600080600080608085870312156131c257600080fd5b6131cb85613074565b966020860135965060408601359560600135945092505050565b6020808252825182820181905260009190848201906040850190845b8181101561321d57835183529284019291840191600101613201565b50909695505050505050565b60006020828403121561323b57600080fd5b61131482613074565b60008060006060848603121561325957600080fd5b61326284613074565b925061327060208501613074565b9150604084013590509250925092565b60006020828403121561329257600080fd5b5035919050565b6000806000606084860312156132ae57600080fd5b6132b784613074565b95602085013595506040909401359392505050565b600080600080600080600060e0888a0312156132e757600080fd5b6132f088613074565b96506132fe60208901613074565b95506040880135945060608801359350608088013560ff8116811461332257600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561335257600080fd5b61335b83613074565b915061336960208401613074565b90509250929050565b6000806040838503121561338557600080fd5b8235915061336960208401613074565b6000602082840312156133a757600080fd5b8151801515811461131457600080fd5b60018060a01b038616815284602082015283604082015260806060820152816080820152818360a0830137600081830160a090810191909152601f909201601f19160101949350505050565b60006020828403121561341557600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b6000828210156134445761344461341c565b500390565b60008160001904831182151516156134635761346361341c565b500290565b60008261348557634e487b7160e01b600052601260045260246000fd5b500490565b600181811c9082168061349e57607f821691505b60208210811415612bb757634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b600082198211156134e8576134e861341c565b500190565b634e487b7160e01b600052603260045260246000fd5b60006000198214156135175761351761341c565b5060010190565b600080835481600182811c91508083168061353a57607f831692505b602080841082141561355a57634e487b7160e01b86526022600452602486fd5b81801561356e576001811461357f576135ac565b60ff198616895284890196506135ac565b60008a81526020902060005b868110156135a45781548b82015290850190830161358b565b505084890196505b509498975050505050505050565b600082516135cc81846020870161311f565b919091019291505056fea264697066735822122012bbc26e6d059c64971894076e1c061b5af33828e3ac3045418303c8201d1fe764736f6c634300080b003360e060405234801561001057600080fd5b5060405161037038038061037083398101604081905261002f91610066565b336080526001600160a01b0391821660a0521660c052610099565b80516001600160a01b038116811461006157600080fd5b919050565b6000806040838503121561007957600080fd5b6100828361004a565b91506100906020840161004a565b90509250929050565b60805160a05160c0516102ab6100c5600039600060b601526000608501526000605001526102ab6000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063533cf5ce14610030575b600080fd5b61004361003e3660046101d0565b610045565b005b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461007a57600080fd5b81156100ab576100ab7f000000000000000000000000000000000000000000000000000000000000000084846100e1565b80156100dc576100dc7f000000000000000000000000000000000000000000000000000000000000000084836100e1565b505050565b6000836001600160a01b03163b116100f857600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916101549190610211565b6000604051808303816000865af19150503d8060008114610191576040519150601f19603f3d011682016040523d82523d6000602084013e610196565b606091505b50915091508180156101c05750805115806101c05750808060200190518101906101c0919061024c565b6101c957600080fd5b5050505050565b6000806000606084860312156101e557600080fd5b83356001600160a01b03811681146101fc57600080fd5b95602085013595506040909401359392505050565b6000825160005b818110156102325760208186018101518583015201610218565b81811115610241576000828501525b509190910192915050565b60006020828403121561025e57600080fd5b8151801515811461026e57600080fd5b939250505056fea26469706673582212205f5c908ef3326a953bd870e8659b0596ff46fae086e9667d4d69778578e14adc64736f6c634300080b0033a2646970667358221220d044256e297a575c8be24751aa6c5b37af7db2fd6b3617dfa590cf6bacfad43964736f6c634300080b0033", + "linkReferences": {}, + "deployedLinkReferences": {} + } \ No newline at end of file diff --git a/external/abi/velodrome/VelodromePair.json b/external/abi/velodrome/VelodromePair.json new file mode 100644 index 000000000..1bc237fd9 --- /dev/null +++ b/external/abi/velodrome/VelodromePair.json @@ -0,0 +1,8 @@ +{ + "contractName": "VelodromePair", + "abi": [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Fees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount0Out","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1Out","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reserve0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reserve1","type":"uint256"}],"name":"Sync","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"blockTimestampLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"burn","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimFees","outputs":[{"internalType":"uint256","name":"claimed0","type":"uint256"},{"internalType":"uint256","name":"claimed1","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimable0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimable1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"current","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentCumulativePrices","outputs":[{"internalType":"uint256","name":"reserve0Cumulative","type":"uint256"},{"internalType":"uint256","name":"reserve1Cumulative","type":"uint256"},{"internalType":"uint256","name":"blockTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fees","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenIn","type":"address"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserves","outputs":[{"internalType":"uint256","name":"_reserve0","type":"uint256"},{"internalType":"uint256","name":"_reserve1","type":"uint256"},{"internalType":"uint256","name":"_blockTimestampLast","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"index0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"index1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastObservation","outputs":[{"components":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"reserve0Cumulative","type":"uint256"},{"internalType":"uint256","name":"reserve1Cumulative","type":"uint256"}],"internalType":"struct Pair.Observation","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadata","outputs":[{"internalType":"uint256","name":"dec0","type":"uint256"},{"internalType":"uint256","name":"dec1","type":"uint256"},{"internalType":"uint256","name":"r0","type":"uint256"},{"internalType":"uint256","name":"r1","type":"uint256"},{"internalType":"bool","name":"st","type":"bool"},{"internalType":"address","name":"t0","type":"address"},{"internalType":"address","name":"t1","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"observationLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"observations","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"reserve0Cumulative","type":"uint256"},{"internalType":"uint256","name":"reserve1Cumulative","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"points","type":"uint256"}],"name":"prices","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"granularity","type":"uint256"}],"name":"quote","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reserve0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reserve0CumulativeLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reserve1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reserve1CumulativeLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"points","type":"uint256"},{"internalType":"uint256","name":"window","type":"uint256"}],"name":"sample","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"skim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"supplyIndex0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"supplyIndex1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount0Out","type":"uint256"},{"internalType":"uint256","name":"amount1Out","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokens","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}], + "bytecode": "0x61016060405260006002556000600d556000600e5560016013553480156200002657600080fd5b50336001600160a01b0316610100816001600160a01b0316815250506000806000336001600160a01b031663eb13c4cf6040518163ffffffff1660e01b8152600401606060405180830381865afa15801562000086573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000ac9190620007c8565b8015156080526001600160a01b0380831660c052831660a052604051929550909350915083908390620000df90620006f7565b6001600160a01b03928316815291166020820152604001604051809103906000f08015801562000113573d6000803e3d6000fd5b506001600160a01b031660e05280156200034557826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000166573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000190919081019062000863565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620001cf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620001f9919081019062000863565b6040516020016200020c9291906200091b565b604051602081830303815290604052600090805190602001906200023292919062000705565b50826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000272573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526200029c919081019062000863565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620002db573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000305919081019062000863565b6040516020016200031892919062000976565b604051602081830303815290604052600190805190602001906200033e92919062000705565b506200055e565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000384573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620003ae919081019062000863565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620003ed573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000417919081019062000863565b6040516020016200042a929190620009c7565b604051602081830303815290604052600090805190602001906200045092919062000705565b50826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000490573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620004ba919081019062000863565b826001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620004f9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405262000523919081019062000863565b6040516020016200053692919062000a24565b604051602081830303815290604052600190805190602001906200055c92919062000705565b505b826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200059d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620005c3919062000a46565b620005d090600a62000b87565b6101208181525050816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000617573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200063d919062000a46565b6200064a90600a62000b87565b6101405250506040805160608101825242815260006020820181815292820181815260078054600181018255925291517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68860039092029182015591517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c689830155517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68a909101555062000bd5565b610370806200436283390190565b828054620007139062000b98565b90600052602060002090601f01602090048101928262000737576000855562000782565b82601f106200075257805160ff191683800117855562000782565b8280016001018555821562000782579182015b828111156200078257825182559160200191906001019062000765565b506200079092915062000794565b5090565b5b8082111562000790576000815560010162000795565b80516001600160a01b0381168114620007c357600080fd5b919050565b600080600060608486031215620007de57600080fd5b620007e984620007ab565b9250620007f960208501620007ab565b9150604084015180151581146200080f57600080fd5b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b60005b838110156200084d57818101518382015260200162000833565b838111156200085d576000848401525b50505050565b6000602082840312156200087657600080fd5b81516001600160401b03808211156200088e57600080fd5b818401915084601f830112620008a357600080fd5b815181811115620008b857620008b86200081a565b604051601f8201601f19908116603f01168101908382118183101715620008e357620008e36200081a565b81604052828152876020848701011115620008fd57600080fd5b6200091083602083016020880162000830565b979650505050505050565b6e029ba30b13632ab189020a6a690169608d1b8152600083516200094781600f85016020880162000830565b602f60f81b600f9184019182015283516200096a81601084016020880162000830565b01601001949350505050565b6473414d4d2d60d81b8152600083516200099881600585016020880162000830565b602f60f81b6005918401918201528351620009bb81600684016020880162000830565b01600601949350505050565b7002b37b630ba34b632ab189020a6a690169607d1b815260008351620009f581601185016020880162000830565b602f60f81b601191840191820152835162000a1881601284016020880162000830565b01601201949350505050565b6476414d4d2d60d81b8152600083516200099881600585016020880162000830565b60006020828403121562000a5957600080fd5b815160ff8116811462000a6b57600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b600181815b8085111562000ac957816000190482111562000aad5762000aad62000a72565b8085161562000abb57918102915b93841c939080029062000a8d565b509250929050565b60008262000ae25750600162000b81565b8162000af15750600062000b81565b816001811462000b0a576002811462000b155762000b35565b600191505062000b81565b60ff84111562000b295762000b2962000a72565b50506001821b62000b81565b5060208310610133831016604e8410600b841016171562000b5a575081810a62000b81565b62000b66838362000a88565b806000190482111562000b7d5762000b7d62000a72565b0290505b92915050565b600062000a6b60ff84168362000ad1565b600181811c9082168062000bad57607f821691505b6020821081141562000bcf57634e487b7160e01b600052602260045260246000fd5b50919050565b60805160a05160c05160e05161010051610120516101405161360c62000d5660003960008181610431015281816125340152818161281e015281816128e001526129eb01526000818161040e015281816124f3015281816127df0152818161292201526129c50152600081816107b801528181610c0701528181610cd801526120630152600081816105f201528181611c770152818161235a01526124270152600081816104bb0152818161064701528181610714015281816108f301528181610b9b01528181611510015281816116f701528181611b1e015281816121c001526124060152600081816102f90152818161049301528181610622015281816108d201528181610b7a0152818161147a015281816116d501528181611afc015281816121380152818161233901528181612860015281816128a70152818161298c0152612a2f0152600081816103970152818161046301528181610bcf01528181610ca001528181612032015281816124cb01526127ab015261360c6000f3fe608060405234801561001057600080fd5b50600436106102745760003560e01c80637ecebe0011610151578063bda39cad116100c3578063d294f09311610087578063d294f09314610736578063d505accf1461073e578063dd62ed3e14610751578063ebeb31db1461077c578063f140a35a14610784578063fff6cae91461079757600080fd5b8063bda39cad146106eb578063bf944dbc146106f4578063c245febc146106fd578063c5700a0214610706578063d21220a71461070f57600080fd5b80639d63848a116101155780639d63848a146106145780639e8cc04b146106725780639f767c8814610685578063a1ac4d13146106a5578063a9059cbb146106c5578063bc25cf77146106d857600080fd5b80637ecebe001461057357806389afcb44146105935780638a7b8cf2146105bb57806395d89b41146105e55780639af1d35a146105ed57600080fd5b8063252c09d7116101ea5780634d5a9f8a116101ae5780634d5a9f8a146104f1578063517b3f82146105115780635881c475146105245780635a76f25e146105375780636a6278421461054057806370a082311461055357600080fd5b8063252c09d7146103cc578063313ce567146103df57806332c0defd146103f9578063392f37e914610402578063443cb4bc146104e857600080fd5b806313345fe11161023c57806313345fe11461033357806318160ddd146103535780631df8c7171461036a578063205aabf11461037257806322be3de11461039257806323b872dd146103b957600080fd5b8063022c0d9f1461027957806306fdde031461028e5780630902f1ac146102ac578063095ea7b3146102d15780630dfe1681146102f4575b600080fd5b61028c61028736600461308b565b61079f565b005b610296610eec565b6040516102a3919061314f565b60405180910390f35b600854600954600a545b604080519384526020840192909252908201526060016102a3565b6102e46102df366004613182565b610f7a565b60405190151581526020016102a3565b61031b7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016102a3565b6103466103413660046131ac565b610fe7565b6040516102a391906131e5565b61035c60025481565b6040519081526020016102a3565b6102b66111e3565b61035c610380366004613229565b60106020526000908152604090205481565b6102e47f000000000000000000000000000000000000000000000000000000000000000081565b6102e46103c7366004613244565b611252565b6102b66103da366004613280565b61131b565b6103e7601281565b60405160ff90911681526020016102a3565b61035c600d5481565b600854600954604080517f000000000000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060208201529081019290925260608201527f0000000000000000000000000000000000000000000000000000000000000000151560808201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660a08301527f00000000000000000000000000000000000000000000000000000000000000001660c082015260e0016102a3565b61035c60085481565b61035c6104ff366004613229565b60116020526000908152604090205481565b61035c61051f366004613182565b61134e565b610346610532366004613299565b611437565b61035c60095481565b61035c61054e366004613229565b611446565b61035c610561366004613229565b60046020526000908152604090205481565b61035c610581366004613229565b60066020526000908152604090205481565b6105a66105a1366004613229565b6116a3565b604080519283526020830191909152016102a3565b6105c36119c0565b60408051825181526020808401519082015291810151908201526060016102a3565b610296611a40565b61031b7f000000000000000000000000000000000000000000000000000000000000000081565b604080516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000166020820152016102a3565b61035c610680366004613299565b611a4d565b61035c610693366004613229565b600f6020526000908152604090205481565b61035c6106b3366004613229565b60126020526000908152604090205481565b6102e46106d3366004613182565b611aba565b61028c6106e6366004613229565b611ad0565b61035c600e5481565b61035c600b5481565b61035c600c5481565b61035c600a5481565b61031b7f000000000000000000000000000000000000000000000000000000000000000081565b6105a6611bee565b61028c61074c3660046132cc565b611d15565b61035c61075f36600461333f565b600360209081526000928352604080842090915290825290205481565b60075461035c565b61035c610792366004613372565b61201d565b61028c61210b565b6013546001146107ae57600080fd5b60026013819055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b187bd266040518163ffffffff1660e01b8152600401602060405180830381865afa158015610814573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108389190613395565b1561084257600080fd5b60008511806108515750600084115b6108885760405162461bcd60e51b8152602060048201526003602482015262494f4160e81b60448201526064015b60405180910390fd5b600854600954818710801561089c57508086105b6108cd5760405162461bcd60e51b8152602060048201526002602482015261125360f21b604482015260640161087f565b6000807f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03898116908316148015906109405750806001600160a01b0316896001600160a01b031614155b6109715760405162461bcd60e51b8152602060048201526002602482015261125560f21b604482015260640161087f565b8a1561098257610982828a8d612245565b891561099357610993818a8c612245565b8615610a0057604051639a7bff7960e01b81526001600160a01b038a1690639a7bff79906109cd9033908f908f908e908e906004016133b7565b600060405180830381600087803b1580156109e757600080fd5b505af11580156109fb573d6000803e3d6000fd5b505050505b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015610a44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a689190613403565b6040516370a0823160e01b81523060048201529094506001600160a01b038216906370a0823190602401602060405180830381865afa158015610aaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad39190613403565b9250505060008985610ae59190613432565b8311610af2576000610b06565b610afc8a86613432565b610b069084613432565b90506000610b148a86613432565b8311610b21576000610b35565b610b2b8a86613432565b610b359084613432565b90506000821180610b465750600081115b610b785760405162461bcd60e51b815260206004820152600360248201526249494160e81b604482015260640161087f565b7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008315610c8b57604051632895a2f560e11b81527f000000000000000000000000000000000000000000000000000000000000000015156004820152610c8b90612710906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063512b45ea90602401602060405180830381865afa158015610c4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c729190613403565b610c7c9087613449565b610c869190613468565b612334565b8215610d5c57604051632895a2f560e11b81527f000000000000000000000000000000000000000000000000000000000000000015156004820152610d5c90612710906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063512b45ea90602401602060405180830381865afa158015610d1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d439190613403565b610d4d9086613449565b610d579190613468565b612401565b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015610da0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc49190613403565b6040516370a0823160e01b81523060048201529096506001600160a01b038216906370a0823190602401602060405180830381865afa158015610e0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2f9190613403565b9450610e3b88886124c7565b610e4587876124c7565b1015610e775760405162461bcd60e51b81526020600482015260016024820152604b60f81b604482015260640161087f565b5050610e8584848888612613565b60408051838152602081018390529081018c9052606081018b90526001600160a01b038a169033907fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d8229060800160405180910390a350506001601355505050505050505050565b60008054610ef99061348a565b80601f0160208091040260200160405190810160405280929190818152602001828054610f259061348a565b8015610f725780601f10610f4757610100808354040283529160200191610f72565b820191906000526020600020905b815481529060010190602001808311610f5557829003601f168201915b505050505081565b3360008181526003602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610fd59086815260200190565b60405180910390a35060015b92915050565b606060008367ffffffffffffffff811115611004576110046134bf565b60405190808252806020026020018201604052801561102d578160200160208202803683370190505b5060075490915060009061104390600190613432565b905060006110518587613449565b61105b9083613432565b90506000805b838310156111d35761107387846134d5565b915060006007848154811061108a5761108a6134ed565b906000526020600020906003020160000154600784815481106110af576110af6134ed565b9060005260206000209060030201600001546110cb9190613432565b9050600081600786815481106110e3576110e36134ed565b90600052602060002090600302016001015460078681548110611108576111086134ed565b9060005260206000209060030201600101546111249190613432565b61112e9190613468565b905060008260078781548110611146576111466134ed565b9060005260206000209060030201600201546007878154811061116b5761116b6134ed565b9060005260206000209060030201600201546111879190613432565b6111919190613468565b905061119f8c8e84846127a7565b8885815181106111b1576111b16134ed565b60209081029190910101525050506001016111cc87846134d5565b9250611061565b509293505050505b949350505050565b600b54600c544260008080611201600854600954600a549192909190565b92509250925083811461124a57600061121a8286613432565b90506112268185613449565b61123090886134d5565b965061123c8184613449565b61124690876134d5565b9550505b505050909192565b6001600160a01b03831660008181526003602090815260408083203380855292528220549192909190821480159061128c57506000198114155b1561130257600061129d8583613432565b6001600160a01b038881166000818152600360209081526040808320948916808452948252918290208590559051848152939450919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505b61130d868686612a9c565b6001925050505b9392505050565b6007818154811061132b57600080fd5b600091825260209091206003909102018054600182015460029092015490925083565b6000806113596119c0565b90506000806113666111e3565b50845191935091504214156113cf576007805461138590600290613432565b81548110611395576113956134ed565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505092505b82516000906113de9042613432565b90506000818560200151856113f39190613432565b6113fd9190613468565b90506000828660400151856114129190613432565b61141c9190613468565b905061142a888a84846127a7565b9998505050505050505050565b60606111db8484846001610fe7565b600060135460011461145757600080fd5b60026013556008546009546040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156114c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114ed9190613403565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157b9190613403565b905060006115898584613432565b905060006115978584613432565b600254909150806115d5576103e86115b76115b28486613449565b612b5c565b6115c19190613432565b97506115d060006103e8612bcc565b61160a565b611607876115e38386613449565b6115ed9190613468565b876115f88486613449565b6116029190613468565b612c5f565b97505b600088116116405760405162461bcd60e51b8152602060048201526003602482015262494c4d60e81b604482015260640161087f565b61164a8989612bcc565b61165685858989612613565b604080518481526020810184905233917f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f910160405180910390a250506001601355509395945050505050565b6000806013546001146116b557600080fd5b60026013556008546009546040516370a0823160e01b81523060048201527f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000906000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561174b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176f9190613403565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156117b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117dd9190613403565b3060009081526004602052604090205460025491925090806117ff8584613449565b6118099190613468565b9950806118168484613449565b6118209190613468565b985060008a1180156118325750600089115b6118645760405162461bcd60e51b815260206004820152600360248201526224a62160e91b604482015260640161087f565b61186e3083612c75565b611879868c8c612245565b611884858c8b612245565b6040516370a0823160e01b81523060048201526001600160a01b038716906370a0823190602401602060405180830381865afa1580156118c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ec9190613403565b6040516370a0823160e01b81523060048201529094506001600160a01b038616906370a0823190602401602060405180830381865afa158015611933573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119579190613403565b925061196584848a8a612613565b604080518b8152602081018b90526001600160a01b038d169133917fdccd412f0b1252819cb1fd330b93224ca42612892bb3f4f789976e6d81936496910160405180910390a350505050505050506001601381905550915091565b6119e460405180606001604052806000815260200160008152602001600081525090565b600780546119f490600190613432565b81548110611a0457611a046134ed565b90600052602060002090600302016040518060600160405290816000820154815260200160018201548152602001600282015481525050905090565b60018054610ef99061348a565b600080611a5d8585856001610fe7565b90506000805b8251811015611aa557828181518110611a7e57611a7e6134ed565b602002602001015182611a9191906134d5565b915080611a9d81613503565b915050611a63565b50611ab08482613468565b9695505050505050565b6000611ac7338484612a9c565b50600192915050565b601354600114611adf57600080fd5b60026013556008546040516370a0823160e01b81523060048201527f0000000000000000000000000000000000000000000000000000000000000000917f000000000000000000000000000000000000000000000000000000000000000091611bac9184918691906001600160a01b038416906370a08231906024015b602060405180830381865afa158015611b79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b9d9190613403565b611ba79190613432565b612245565b6009546040516370a0823160e01b8152306004820152611be49183918691906001600160a01b038416906370a0823190602401611b5c565b5050600160135550565b600080611bfa33612d00565b50503360009081526011602090815260408083205460129092529091205481151580611c265750600081115b15611d11573360008181526011602090815260408083208390556012909152808220919091555163299e7ae760e11b8152600481019190915260248101839052604481018290526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063533cf5ce90606401600060405180830381600087803b158015611cbb57600080fd5b505af1158015611ccf573d6000803e3d6000fd5b505060408051858152602081018590523393508392507f865ca08d59f5cb456e85cd2f7ef63664ea4f73327414e9d8152c4158b0e94645910160405180910390a35b9091565b42841015611d555760405162461bcd60e51b815260206004820152600d60248201526c14185a5c8e8811561412549151609a1b604482015260640161087f565b7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051611d85919061351e565b60408051918290038220828201825260018352603160f81b6020938401528151928301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160408051601f19818403018152918152815160209283012060058190556001600160a01b038a166000908152600690935290822080547f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918b918b918b919087611e5083613503565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810187905260e00160405160208183030381529060405280519060200120604051602001611ec992919061190160f01b81526002810192909252602282015260420190565b60408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015611f34573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615801590611f6a5750886001600160a01b0316816001600160a01b0316145b611fb65760405162461bcd60e51b815260206004820152601760248201527f506169723a20494e56414c49445f5349474e4154555245000000000000000000604482015260640161087f565b6001600160a01b038981166000818152600360209081526040808320948d16808452948252918290208b905590518a81527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050505050505050565b600854600954604051632895a2f560e11b81527f0000000000000000000000000000000000000000000000000000000000000000151560048201526000929190612710907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063512b45ea90602401602060405180830381865afa1580156120b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120d69190613403565b6120e09087613449565b6120ea9190613468565b6120f49086613432565b9450612102858584846127a7565b95945050505050565b60135460011461211a57600080fd5b60026013556040516370a0823160e01b815230600482015261223e907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612187573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121ab9190613403565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561220f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122339190613403565b600854600954612613565b6001601355565b6000836001600160a01b03163b1161225c57600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916122b891906135ba565b6000604051808303816000865af19150503d80600081146122f5576040519150601f19603f3d011682016040523d82523d6000602084013e6122fa565b606091505b50915091508180156123245750805115806123245750808060200190518101906123249190613395565b61232d57600080fd5b5050505050565b61237f7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083612245565b60025460009061239783670de0b6b3a7640000613449565b6123a19190613468565b905080156123c15780600d60008282546123bb91906134d5565b90915550505b604080518381526000602082015233917f112c256902bf554b6ed882d2936687aaeb4225e8cd5b51303c90ca6cf43a860291015b60405180910390a25050565b61244c7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083612245565b60025460009061246483670de0b6b3a7640000613449565b61246e9190613468565b9050801561248e5780600e600082825461248891906134d5565b90915550505b60408051600081526020810184905233917f112c256902bf554b6ed882d2936687aaeb4225e8cd5b51303c90ca6cf43a860291016123f5565b60007f0000000000000000000000000000000000000000000000000000000000000000156126025760007f000000000000000000000000000000000000000000000000000000000000000061252485670de0b6b3a7640000613449565b61252e9190613468565b905060007f000000000000000000000000000000000000000000000000000000000000000061256585670de0b6b3a7640000613449565b61256f9190613468565b90506000670de0b6b3a76400006125868385613449565b6125909190613468565b90506000670de0b6b3a76400006125a78480613449565b6125b19190613468565b670de0b6b3a76400006125c48680613449565b6125ce9190613468565b6125d891906134d5565b9050670de0b6b3a76400006125ed8284613449565b6125f79190613468565b945050505050610fe1565b61260c8284613449565b9050610fe1565b600a5442906000906126259083613432565b905060008111801561263657508315155b801561264157508215155b15612688576126508185613449565b600b600082825461266191906134d5565b9091555061267190508184613449565b600c600082825461268291906134d5565b90915550505b60006126926119c0565b80519091506126a19084613432565b91506107088211156127565760408051606081018252848152600b5460208201908152600c549282019283526007805460018101825560009190915291517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688600390930292830155517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68982015590517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68a909101555b60088790556009869055600a83905560408051888152602081018890527fcf2aa50876cdfbb541206f89af0ee78d44a2abf8d328e37fa4917f982149848a910160405180910390a150505050505050565b60007f000000000000000000000000000000000000000000000000000000000000000015612a2a5760006127db84846124c7565b90507f000000000000000000000000000000000000000000000000000000000000000061281085670de0b6b3a7640000613449565b61281a9190613468565b93507f000000000000000000000000000000000000000000000000000000000000000061284f84670de0b6b3a7640000613449565b6128599190613468565b92506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b03161461289e5784866128a1565b85855b915091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b031614612920577f000000000000000000000000000000000000000000000000000000000000000061291189670de0b6b3a7640000613449565b61291b9190613468565b61295d565b7f000000000000000000000000000000000000000000000000000000000000000061295389670de0b6b3a7640000613449565b61295d9190613468565b9750600061297561296e848b6134d5565b8584612e60565b61297f9083613432565b9050670de0b6b3a76400007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316896001600160a01b0316146129e9577f0000000000000000000000000000000000000000000000000000000000000000612a0b565b7f00000000000000000000000000000000000000000000000000000000000000005b612a159083613449565b612a1f9190613468565b9450505050506111db565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316866001600160a01b031614612a6d578385612a70565b84845b9092509050612a7f87836134d5565b612a898289613449565b612a939190613468565b925050506111db565b612aa583612d00565b612aae82612d00565b6001600160a01b03831660009081526004602052604081208054839290612ad6908490613432565b90915550506001600160a01b03821660009081526004602052604081208054839290612b039084906134d5565b92505081905550816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051612b4f91815260200190565b60405180910390a3505050565b60006003821115612bbd5750806000612b76600283613468565b612b819060016134d5565b90505b81811015612bb757905080600281612b9c8186613468565b612ba691906134d5565b612bb09190613468565b9050612b84565b50919050565b8115612bc7575060015b919050565b612bd582612d00565b8060026000828254612be791906134d5565b90915550506001600160a01b03821660009081526004602052604081208054839290612c149084906134d5565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b6000818310612c6e5781611314565b5090919050565b612c7e82612d00565b8060026000828254612c909190613432565b90915550506001600160a01b03821660009081526004602052604081208054839290612cbd908490613432565b90915550506040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001612c53565b6001600160a01b0381166000908152600460205260409020548015612e2e576001600160a01b0382166000908152600f60209081526040808320805460108085529285208054600d54600e54948190559490955282905593612d628584613432565b90506000612d708584613432565b90508115612dcb576000670de0b6b3a7640000612d8d848a613449565b612d979190613468565b6001600160a01b038a16600090815260116020526040812080549293508392909190612dc49084906134d5565b9091555050505b8015612e24576000670de0b6b3a7640000612de6838a613449565b612df09190613468565b6001600160a01b038a16600090815260126020526040812080549293508392909190612e1d9084906134d5565b9091555050505b5050505050505050565b600d546001600160a01b0383166000908152600f6020908152604080832093909355600e546010909152919020555050565b6000805b60ff811015612f6657826000612e7a8783612f6f565b905085811015612eca576000612e90888761300c565b612e9a8389613432565b612eac90670de0b6b3a7640000613449565b612eb69190613468565b9050612ec281876134d5565b955050612f0c565b6000612ed6888761300c565b612ee08884613432565b612ef290670de0b6b3a7640000613449565b612efc9190613468565b9050612f088187613432565b9550505b81851115612f35576001612f208387613432565b11612f3057849350505050611314565b612f51565b6001612f418684613432565b11612f5157849350505050611314565b50508080612f5e90613503565b915050612e64565b50909392505050565b6000670de0b6b3a764000082818581612f888280613449565b612f929190613468565b612f9c9190613449565b612fa69190613468565b612fb09190613449565b612fba9190613468565b670de0b6b3a7640000808481612fd08280613449565b612fda9190613468565b612fe49190613449565b612fee9190613468565b612ff89086613449565b6130029190613468565b61131491906134d5565b6000670de0b6b3a764000083816130238280613449565b61302d9190613468565b6130379190613449565b6130419190613468565b670de0b6b3a7640000806130558580613449565b61305f9190613468565b61306a866003613449565b612ff89190613449565b80356001600160a01b0381168114612bc757600080fd5b6000806000806000608086880312156130a357600080fd5b85359450602086013593506130ba60408701613074565b9250606086013567ffffffffffffffff808211156130d757600080fd5b818801915088601f8301126130eb57600080fd5b8135818111156130fa57600080fd5b89602082850101111561310c57600080fd5b9699959850939650602001949392505050565b60005b8381101561313a578181015183820152602001613122565b83811115613149576000848401525b50505050565b602081526000825180602084015261316e81604085016020870161311f565b601f01601f19169190910160400192915050565b6000806040838503121561319557600080fd5b61319e83613074565b946020939093013593505050565b600080600080608085870312156131c257600080fd5b6131cb85613074565b966020860135965060408601359560600135945092505050565b6020808252825182820181905260009190848201906040850190845b8181101561321d57835183529284019291840191600101613201565b50909695505050505050565b60006020828403121561323b57600080fd5b61131482613074565b60008060006060848603121561325957600080fd5b61326284613074565b925061327060208501613074565b9150604084013590509250925092565b60006020828403121561329257600080fd5b5035919050565b6000806000606084860312156132ae57600080fd5b6132b784613074565b95602085013595506040909401359392505050565b600080600080600080600060e0888a0312156132e757600080fd5b6132f088613074565b96506132fe60208901613074565b95506040880135945060608801359350608088013560ff8116811461332257600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561335257600080fd5b61335b83613074565b915061336960208401613074565b90509250929050565b6000806040838503121561338557600080fd5b8235915061336960208401613074565b6000602082840312156133a757600080fd5b8151801515811461131457600080fd5b60018060a01b038616815284602082015283604082015260806060820152816080820152818360a0830137600081830160a090810191909152601f909201601f19160101949350505050565b60006020828403121561341557600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b6000828210156134445761344461341c565b500390565b60008160001904831182151516156134635761346361341c565b500290565b60008261348557634e487b7160e01b600052601260045260246000fd5b500490565b600181811c9082168061349e57607f821691505b60208210811415612bb757634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b600082198211156134e8576134e861341c565b500190565b634e487b7160e01b600052603260045260246000fd5b60006000198214156135175761351761341c565b5060010190565b600080835481600182811c91508083168061353a57607f831692505b602080841082141561355a57634e487b7160e01b86526022600452602486fd5b81801561356e576001811461357f576135ac565b60ff198616895284890196506135ac565b60008a81526020902060005b868110156135a45781548b82015290850190830161358b565b505084890196505b509498975050505050505050565b600082516135cc81846020870161311f565b919091019291505056fea264697066735822122012bbc26e6d059c64971894076e1c061b5af33828e3ac3045418303c8201d1fe764736f6c634300080b003360e060405234801561001057600080fd5b5060405161037038038061037083398101604081905261002f91610066565b336080526001600160a01b0391821660a0521660c052610099565b80516001600160a01b038116811461006157600080fd5b919050565b6000806040838503121561007957600080fd5b6100828361004a565b91506100906020840161004a565b90509250929050565b60805160a05160c0516102ab6100c5600039600060b601526000608501526000605001526102ab6000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063533cf5ce14610030575b600080fd5b61004361003e3660046101d0565b610045565b005b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461007a57600080fd5b81156100ab576100ab7f000000000000000000000000000000000000000000000000000000000000000084846100e1565b80156100dc576100dc7f000000000000000000000000000000000000000000000000000000000000000084836100e1565b505050565b6000836001600160a01b03163b116100f857600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916101549190610211565b6000604051808303816000865af19150503d8060008114610191576040519150601f19603f3d011682016040523d82523d6000602084013e610196565b606091505b50915091508180156101c05750805115806101c05750808060200190518101906101c0919061024c565b6101c957600080fd5b5050505050565b6000806000606084860312156101e557600080fd5b83356001600160a01b03811681146101fc57600080fd5b95602085013595506040909401359392505050565b6000825160005b818110156102325760208186018101518583015201610218565b81811115610241576000828501525b509190910192915050565b60006020828403121561025e57600080fd5b8151801515811461026e57600080fd5b939250505056fea26469706673582212205f5c908ef3326a953bd870e8659b0596ff46fae086e9667d4d69778578e14adc64736f6c634300080b0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106102745760003560e01c80637ecebe0011610151578063bda39cad116100c3578063d294f09311610087578063d294f09314610736578063d505accf1461073e578063dd62ed3e14610751578063ebeb31db1461077c578063f140a35a14610784578063fff6cae91461079757600080fd5b8063bda39cad146106eb578063bf944dbc146106f4578063c245febc146106fd578063c5700a0214610706578063d21220a71461070f57600080fd5b80639d63848a116101155780639d63848a146106145780639e8cc04b146106725780639f767c8814610685578063a1ac4d13146106a5578063a9059cbb146106c5578063bc25cf77146106d857600080fd5b80637ecebe001461057357806389afcb44146105935780638a7b8cf2146105bb57806395d89b41146105e55780639af1d35a146105ed57600080fd5b8063252c09d7116101ea5780634d5a9f8a116101ae5780634d5a9f8a146104f1578063517b3f82146105115780635881c475146105245780635a76f25e146105375780636a6278421461054057806370a082311461055357600080fd5b8063252c09d7146103cc578063313ce567146103df57806332c0defd146103f9578063392f37e914610402578063443cb4bc146104e857600080fd5b806313345fe11161023c57806313345fe11461033357806318160ddd146103535780631df8c7171461036a578063205aabf11461037257806322be3de11461039257806323b872dd146103b957600080fd5b8063022c0d9f1461027957806306fdde031461028e5780630902f1ac146102ac578063095ea7b3146102d15780630dfe1681146102f4575b600080fd5b61028c61028736600461308b565b61079f565b005b610296610eec565b6040516102a3919061314f565b60405180910390f35b600854600954600a545b604080519384526020840192909252908201526060016102a3565b6102e46102df366004613182565b610f7a565b60405190151581526020016102a3565b61031b7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016102a3565b6103466103413660046131ac565b610fe7565b6040516102a391906131e5565b61035c60025481565b6040519081526020016102a3565b6102b66111e3565b61035c610380366004613229565b60106020526000908152604090205481565b6102e47f000000000000000000000000000000000000000000000000000000000000000081565b6102e46103c7366004613244565b611252565b6102b66103da366004613280565b61131b565b6103e7601281565b60405160ff90911681526020016102a3565b61035c600d5481565b600854600954604080517f000000000000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060208201529081019290925260608201527f0000000000000000000000000000000000000000000000000000000000000000151560808201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660a08301527f00000000000000000000000000000000000000000000000000000000000000001660c082015260e0016102a3565b61035c60085481565b61035c6104ff366004613229565b60116020526000908152604090205481565b61035c61051f366004613182565b61134e565b610346610532366004613299565b611437565b61035c60095481565b61035c61054e366004613229565b611446565b61035c610561366004613229565b60046020526000908152604090205481565b61035c610581366004613229565b60066020526000908152604090205481565b6105a66105a1366004613229565b6116a3565b604080519283526020830191909152016102a3565b6105c36119c0565b60408051825181526020808401519082015291810151908201526060016102a3565b610296611a40565b61031b7f000000000000000000000000000000000000000000000000000000000000000081565b604080516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000166020820152016102a3565b61035c610680366004613299565b611a4d565b61035c610693366004613229565b600f6020526000908152604090205481565b61035c6106b3366004613229565b60126020526000908152604090205481565b6102e46106d3366004613182565b611aba565b61028c6106e6366004613229565b611ad0565b61035c600e5481565b61035c600b5481565b61035c600c5481565b61035c600a5481565b61031b7f000000000000000000000000000000000000000000000000000000000000000081565b6105a6611bee565b61028c61074c3660046132cc565b611d15565b61035c61075f36600461333f565b600360209081526000928352604080842090915290825290205481565b60075461035c565b61035c610792366004613372565b61201d565b61028c61210b565b6013546001146107ae57600080fd5b60026013819055507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b187bd266040518163ffffffff1660e01b8152600401602060405180830381865afa158015610814573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108389190613395565b1561084257600080fd5b60008511806108515750600084115b6108885760405162461bcd60e51b8152602060048201526003602482015262494f4160e81b60448201526064015b60405180910390fd5b600854600954818710801561089c57508086105b6108cd5760405162461bcd60e51b8152602060048201526002602482015261125360f21b604482015260640161087f565b6000807f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03898116908316148015906109405750806001600160a01b0316896001600160a01b031614155b6109715760405162461bcd60e51b8152602060048201526002602482015261125560f21b604482015260640161087f565b8a1561098257610982828a8d612245565b891561099357610993818a8c612245565b8615610a0057604051639a7bff7960e01b81526001600160a01b038a1690639a7bff79906109cd9033908f908f908e908e906004016133b7565b600060405180830381600087803b1580156109e757600080fd5b505af11580156109fb573d6000803e3d6000fd5b505050505b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015610a44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a689190613403565b6040516370a0823160e01b81523060048201529094506001600160a01b038216906370a0823190602401602060405180830381865afa158015610aaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad39190613403565b9250505060008985610ae59190613432565b8311610af2576000610b06565b610afc8a86613432565b610b069084613432565b90506000610b148a86613432565b8311610b21576000610b35565b610b2b8a86613432565b610b359084613432565b90506000821180610b465750600081115b610b785760405162461bcd60e51b815260206004820152600360248201526249494160e81b604482015260640161087f565b7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008315610c8b57604051632895a2f560e11b81527f000000000000000000000000000000000000000000000000000000000000000015156004820152610c8b90612710906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063512b45ea90602401602060405180830381865afa158015610c4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c729190613403565b610c7c9087613449565b610c869190613468565b612334565b8215610d5c57604051632895a2f560e11b81527f000000000000000000000000000000000000000000000000000000000000000015156004820152610d5c90612710906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063512b45ea90602401602060405180830381865afa158015610d1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d439190613403565b610d4d9086613449565b610d579190613468565b612401565b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015610da0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc49190613403565b6040516370a0823160e01b81523060048201529096506001600160a01b038216906370a0823190602401602060405180830381865afa158015610e0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2f9190613403565b9450610e3b88886124c7565b610e4587876124c7565b1015610e775760405162461bcd60e51b81526020600482015260016024820152604b60f81b604482015260640161087f565b5050610e8584848888612613565b60408051838152602081018390529081018c9052606081018b90526001600160a01b038a169033907fd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d8229060800160405180910390a350506001601355505050505050505050565b60008054610ef99061348a565b80601f0160208091040260200160405190810160405280929190818152602001828054610f259061348a565b8015610f725780601f10610f4757610100808354040283529160200191610f72565b820191906000526020600020905b815481529060010190602001808311610f5557829003601f168201915b505050505081565b3360008181526003602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610fd59086815260200190565b60405180910390a35060015b92915050565b606060008367ffffffffffffffff811115611004576110046134bf565b60405190808252806020026020018201604052801561102d578160200160208202803683370190505b5060075490915060009061104390600190613432565b905060006110518587613449565b61105b9083613432565b90506000805b838310156111d35761107387846134d5565b915060006007848154811061108a5761108a6134ed565b906000526020600020906003020160000154600784815481106110af576110af6134ed565b9060005260206000209060030201600001546110cb9190613432565b9050600081600786815481106110e3576110e36134ed565b90600052602060002090600302016001015460078681548110611108576111086134ed565b9060005260206000209060030201600101546111249190613432565b61112e9190613468565b905060008260078781548110611146576111466134ed565b9060005260206000209060030201600201546007878154811061116b5761116b6134ed565b9060005260206000209060030201600201546111879190613432565b6111919190613468565b905061119f8c8e84846127a7565b8885815181106111b1576111b16134ed565b60209081029190910101525050506001016111cc87846134d5565b9250611061565b509293505050505b949350505050565b600b54600c544260008080611201600854600954600a549192909190565b92509250925083811461124a57600061121a8286613432565b90506112268185613449565b61123090886134d5565b965061123c8184613449565b61124690876134d5565b9550505b505050909192565b6001600160a01b03831660008181526003602090815260408083203380855292528220549192909190821480159061128c57506000198114155b1561130257600061129d8583613432565b6001600160a01b038881166000818152600360209081526040808320948916808452948252918290208590559051848152939450919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505b61130d868686612a9c565b6001925050505b9392505050565b6007818154811061132b57600080fd5b600091825260209091206003909102018054600182015460029092015490925083565b6000806113596119c0565b90506000806113666111e3565b50845191935091504214156113cf576007805461138590600290613432565b81548110611395576113956134ed565b9060005260206000209060030201604051806060016040529081600082015481526020016001820154815260200160028201548152505092505b82516000906113de9042613432565b90506000818560200151856113f39190613432565b6113fd9190613468565b90506000828660400151856114129190613432565b61141c9190613468565b905061142a888a84846127a7565b9998505050505050505050565b60606111db8484846001610fe7565b600060135460011461145757600080fd5b60026013556008546009546040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156114c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114ed9190613403565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611557573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157b9190613403565b905060006115898584613432565b905060006115978584613432565b600254909150806115d5576103e86115b76115b28486613449565b612b5c565b6115c19190613432565b97506115d060006103e8612bcc565b61160a565b611607876115e38386613449565b6115ed9190613468565b876115f88486613449565b6116029190613468565b612c5f565b97505b600088116116405760405162461bcd60e51b8152602060048201526003602482015262494c4d60e81b604482015260640161087f565b61164a8989612bcc565b61165685858989612613565b604080518481526020810184905233917f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f910160405180910390a250506001601355509395945050505050565b6000806013546001146116b557600080fd5b60026013556008546009546040516370a0823160e01b81523060048201527f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000906000906001600160a01b038416906370a0823190602401602060405180830381865afa15801561174b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061176f9190613403565b6040516370a0823160e01b81523060048201529091506000906001600160a01b038416906370a0823190602401602060405180830381865afa1580156117b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117dd9190613403565b3060009081526004602052604090205460025491925090806117ff8584613449565b6118099190613468565b9950806118168484613449565b6118209190613468565b985060008a1180156118325750600089115b6118645760405162461bcd60e51b815260206004820152600360248201526224a62160e91b604482015260640161087f565b61186e3083612c75565b611879868c8c612245565b611884858c8b612245565b6040516370a0823160e01b81523060048201526001600160a01b038716906370a0823190602401602060405180830381865afa1580156118c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ec9190613403565b6040516370a0823160e01b81523060048201529094506001600160a01b038616906370a0823190602401602060405180830381865afa158015611933573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119579190613403565b925061196584848a8a612613565b604080518b8152602081018b90526001600160a01b038d169133917fdccd412f0b1252819cb1fd330b93224ca42612892bb3f4f789976e6d81936496910160405180910390a350505050505050506001601381905550915091565b6119e460405180606001604052806000815260200160008152602001600081525090565b600780546119f490600190613432565b81548110611a0457611a046134ed565b90600052602060002090600302016040518060600160405290816000820154815260200160018201548152602001600282015481525050905090565b60018054610ef99061348a565b600080611a5d8585856001610fe7565b90506000805b8251811015611aa557828181518110611a7e57611a7e6134ed565b602002602001015182611a9191906134d5565b915080611a9d81613503565b915050611a63565b50611ab08482613468565b9695505050505050565b6000611ac7338484612a9c565b50600192915050565b601354600114611adf57600080fd5b60026013556008546040516370a0823160e01b81523060048201527f0000000000000000000000000000000000000000000000000000000000000000917f000000000000000000000000000000000000000000000000000000000000000091611bac9184918691906001600160a01b038416906370a08231906024015b602060405180830381865afa158015611b79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b9d9190613403565b611ba79190613432565b612245565b6009546040516370a0823160e01b8152306004820152611be49183918691906001600160a01b038416906370a0823190602401611b5c565b5050600160135550565b600080611bfa33612d00565b50503360009081526011602090815260408083205460129092529091205481151580611c265750600081115b15611d11573360008181526011602090815260408083208390556012909152808220919091555163299e7ae760e11b8152600481019190915260248101839052604481018290526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063533cf5ce90606401600060405180830381600087803b158015611cbb57600080fd5b505af1158015611ccf573d6000803e3d6000fd5b505060408051858152602081018590523393508392507f865ca08d59f5cb456e85cd2f7ef63664ea4f73327414e9d8152c4158b0e94645910160405180910390a35b9091565b42841015611d555760405162461bcd60e51b815260206004820152600d60248201526c14185a5c8e8811561412549151609a1b604482015260640161087f565b7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051611d85919061351e565b60408051918290038220828201825260018352603160f81b6020938401528151928301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160408051601f19818403018152918152815160209283012060058190556001600160a01b038a166000908152600690935290822080547f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918b918b918b919087611e5083613503565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810187905260e00160405160208183030381529060405280519060200120604051602001611ec992919061190160f01b81526002810192909252602282015260420190565b60408051601f198184030181528282528051602091820120600080855291840180845281905260ff88169284019290925260608301869052608083018590529092509060019060a0016020604051602081039080840390855afa158015611f34573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811615801590611f6a5750886001600160a01b0316816001600160a01b0316145b611fb65760405162461bcd60e51b815260206004820152601760248201527f506169723a20494e56414c49445f5349474e4154555245000000000000000000604482015260640161087f565b6001600160a01b038981166000818152600360209081526040808320948d16808452948252918290208b905590518a81527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050505050505050565b600854600954604051632895a2f560e11b81527f0000000000000000000000000000000000000000000000000000000000000000151560048201526000929190612710907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063512b45ea90602401602060405180830381865afa1580156120b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120d69190613403565b6120e09087613449565b6120ea9190613468565b6120f49086613432565b9450612102858584846127a7565b95945050505050565b60135460011461211a57600080fd5b60026013556040516370a0823160e01b815230600482015261223e907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015612187573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121ab9190613403565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561220f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122339190613403565b600854600954612613565b6001601355565b6000836001600160a01b03163b1161225c57600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916122b891906135ba565b6000604051808303816000865af19150503d80600081146122f5576040519150601f19603f3d011682016040523d82523d6000602084013e6122fa565b606091505b50915091508180156123245750805115806123245750808060200190518101906123249190613395565b61232d57600080fd5b5050505050565b61237f7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083612245565b60025460009061239783670de0b6b3a7640000613449565b6123a19190613468565b905080156123c15780600d60008282546123bb91906134d5565b90915550505b604080518381526000602082015233917f112c256902bf554b6ed882d2936687aaeb4225e8cd5b51303c90ca6cf43a860291015b60405180910390a25050565b61244c7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083612245565b60025460009061246483670de0b6b3a7640000613449565b61246e9190613468565b9050801561248e5780600e600082825461248891906134d5565b90915550505b60408051600081526020810184905233917f112c256902bf554b6ed882d2936687aaeb4225e8cd5b51303c90ca6cf43a860291016123f5565b60007f0000000000000000000000000000000000000000000000000000000000000000156126025760007f000000000000000000000000000000000000000000000000000000000000000061252485670de0b6b3a7640000613449565b61252e9190613468565b905060007f000000000000000000000000000000000000000000000000000000000000000061256585670de0b6b3a7640000613449565b61256f9190613468565b90506000670de0b6b3a76400006125868385613449565b6125909190613468565b90506000670de0b6b3a76400006125a78480613449565b6125b19190613468565b670de0b6b3a76400006125c48680613449565b6125ce9190613468565b6125d891906134d5565b9050670de0b6b3a76400006125ed8284613449565b6125f79190613468565b945050505050610fe1565b61260c8284613449565b9050610fe1565b600a5442906000906126259083613432565b905060008111801561263657508315155b801561264157508215155b15612688576126508185613449565b600b600082825461266191906134d5565b9091555061267190508184613449565b600c600082825461268291906134d5565b90915550505b60006126926119c0565b80519091506126a19084613432565b91506107088211156127565760408051606081018252848152600b5460208201908152600c549282019283526007805460018101825560009190915291517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688600390930292830155517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68982015590517fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68a909101555b60088790556009869055600a83905560408051888152602081018890527fcf2aa50876cdfbb541206f89af0ee78d44a2abf8d328e37fa4917f982149848a910160405180910390a150505050505050565b60007f000000000000000000000000000000000000000000000000000000000000000015612a2a5760006127db84846124c7565b90507f000000000000000000000000000000000000000000000000000000000000000061281085670de0b6b3a7640000613449565b61281a9190613468565b93507f000000000000000000000000000000000000000000000000000000000000000061284f84670de0b6b3a7640000613449565b6128599190613468565b92506000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b03161461289e5784866128a1565b85855b915091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316876001600160a01b031614612920577f000000000000000000000000000000000000000000000000000000000000000061291189670de0b6b3a7640000613449565b61291b9190613468565b61295d565b7f000000000000000000000000000000000000000000000000000000000000000061295389670de0b6b3a7640000613449565b61295d9190613468565b9750600061297561296e848b6134d5565b8584612e60565b61297f9083613432565b9050670de0b6b3a76400007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316896001600160a01b0316146129e9577f0000000000000000000000000000000000000000000000000000000000000000612a0b565b7f00000000000000000000000000000000000000000000000000000000000000005b612a159083613449565b612a1f9190613468565b9450505050506111db565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316866001600160a01b031614612a6d578385612a70565b84845b9092509050612a7f87836134d5565b612a898289613449565b612a939190613468565b925050506111db565b612aa583612d00565b612aae82612d00565b6001600160a01b03831660009081526004602052604081208054839290612ad6908490613432565b90915550506001600160a01b03821660009081526004602052604081208054839290612b039084906134d5565b92505081905550816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051612b4f91815260200190565b60405180910390a3505050565b60006003821115612bbd5750806000612b76600283613468565b612b819060016134d5565b90505b81811015612bb757905080600281612b9c8186613468565b612ba691906134d5565b612bb09190613468565b9050612b84565b50919050565b8115612bc7575060015b919050565b612bd582612d00565b8060026000828254612be791906134d5565b90915550506001600160a01b03821660009081526004602052604081208054839290612c149084906134d5565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b6000818310612c6e5781611314565b5090919050565b612c7e82612d00565b8060026000828254612c909190613432565b90915550506001600160a01b03821660009081526004602052604081208054839290612cbd908490613432565b90915550506040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001612c53565b6001600160a01b0381166000908152600460205260409020548015612e2e576001600160a01b0382166000908152600f60209081526040808320805460108085529285208054600d54600e54948190559490955282905593612d628584613432565b90506000612d708584613432565b90508115612dcb576000670de0b6b3a7640000612d8d848a613449565b612d979190613468565b6001600160a01b038a16600090815260116020526040812080549293508392909190612dc49084906134d5565b9091555050505b8015612e24576000670de0b6b3a7640000612de6838a613449565b612df09190613468565b6001600160a01b038a16600090815260126020526040812080549293508392909190612e1d9084906134d5565b9091555050505b5050505050505050565b600d546001600160a01b0383166000908152600f6020908152604080832093909355600e546010909152919020555050565b6000805b60ff811015612f6657826000612e7a8783612f6f565b905085811015612eca576000612e90888761300c565b612e9a8389613432565b612eac90670de0b6b3a7640000613449565b612eb69190613468565b9050612ec281876134d5565b955050612f0c565b6000612ed6888761300c565b612ee08884613432565b612ef290670de0b6b3a7640000613449565b612efc9190613468565b9050612f088187613432565b9550505b81851115612f35576001612f208387613432565b11612f3057849350505050611314565b612f51565b6001612f418684613432565b11612f5157849350505050611314565b50508080612f5e90613503565b915050612e64565b50909392505050565b6000670de0b6b3a764000082818581612f888280613449565b612f929190613468565b612f9c9190613449565b612fa69190613468565b612fb09190613449565b612fba9190613468565b670de0b6b3a7640000808481612fd08280613449565b612fda9190613468565b612fe49190613449565b612fee9190613468565b612ff89086613449565b6130029190613468565b61131491906134d5565b6000670de0b6b3a764000083816130238280613449565b61302d9190613468565b6130379190613449565b6130419190613468565b670de0b6b3a7640000806130558580613449565b61305f9190613468565b61306a866003613449565b612ff89190613449565b80356001600160a01b0381168114612bc757600080fd5b6000806000806000608086880312156130a357600080fd5b85359450602086013593506130ba60408701613074565b9250606086013567ffffffffffffffff808211156130d757600080fd5b818801915088601f8301126130eb57600080fd5b8135818111156130fa57600080fd5b89602082850101111561310c57600080fd5b9699959850939650602001949392505050565b60005b8381101561313a578181015183820152602001613122565b83811115613149576000848401525b50505050565b602081526000825180602084015261316e81604085016020870161311f565b601f01601f19169190910160400192915050565b6000806040838503121561319557600080fd5b61319e83613074565b946020939093013593505050565b600080600080608085870312156131c257600080fd5b6131cb85613074565b966020860135965060408601359560600135945092505050565b6020808252825182820181905260009190848201906040850190845b8181101561321d57835183529284019291840191600101613201565b50909695505050505050565b60006020828403121561323b57600080fd5b61131482613074565b60008060006060848603121561325957600080fd5b61326284613074565b925061327060208501613074565b9150604084013590509250925092565b60006020828403121561329257600080fd5b5035919050565b6000806000606084860312156132ae57600080fd5b6132b784613074565b95602085013595506040909401359392505050565b600080600080600080600060e0888a0312156132e757600080fd5b6132f088613074565b96506132fe60208901613074565b95506040880135945060608801359350608088013560ff8116811461332257600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561335257600080fd5b61335b83613074565b915061336960208401613074565b90509250929050565b6000806040838503121561338557600080fd5b8235915061336960208401613074565b6000602082840312156133a757600080fd5b8151801515811461131457600080fd5b60018060a01b038616815284602082015283604082015260806060820152816080820152818360a0830137600081830160a090810191909152601f909201601f19160101949350505050565b60006020828403121561341557600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b6000828210156134445761344461341c565b500390565b60008160001904831182151516156134635761346361341c565b500290565b60008261348557634e487b7160e01b600052601260045260246000fd5b500490565b600181811c9082168061349e57607f821691505b60208210811415612bb757634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b600082198211156134e8576134e861341c565b500190565b634e487b7160e01b600052603260045260246000fd5b60006000198214156135175761351761341c565b5060010190565b600080835481600182811c91508083168061353a57607f831692505b602080841082141561355a57634e487b7160e01b86526022600452602486fd5b81801561356e576001811461357f576135ac565b60ff198616895284890196506135ac565b60008a81526020902060005b868110156135a45781548b82015290850190830161358b565b505084890196505b509498975050505050505050565b600082516135cc81846020870161311f565b919091019291505056fea264697066735822122012bbc26e6d059c64971894076e1c061b5af33828e3ac3045418303c8201d1fe764736f6c634300080b0033", + "linkReferences": {}, + "deployedLinkReferences": {} + } \ No newline at end of file diff --git a/external/abi/velodrome/VelodromeRouter.json b/external/abi/velodrome/VelodromeRouter.json new file mode 100644 index 000000000..54f1358b2 --- /dev/null +++ b/external/abi/velodrome/VelodromeRouter.json @@ -0,0 +1,8 @@ +{ + "contractName": "VelodromeRouter", + "abi": [{"inputs":[{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address","name":"_weth","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"}],"internalType":"struct Router.route[]","name":"routes","type":"tuple[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"UNSAFE_swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"uint256","name":"amountADesired","type":"uint256"},{"internalType":"uint256","name":"amountBDesired","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"uint256","name":"amountTokenDesired","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"stable","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"}],"internalType":"struct Router.route[]","name":"routes","type":"tuple[]"}],"name":"getAmountsOut","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"}],"name":"getReserves","outputs":[{"internalType":"uint256","name":"reserveA","type":"uint256"},{"internalType":"uint256","name":"reserveB","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pair","type":"address"}],"name":"isPair","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"}],"name":"pairFor","outputs":[{"internalType":"address","name":"pair","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"uint256","name":"amountADesired","type":"uint256"},{"internalType":"uint256","name":"amountBDesired","type":"uint256"}],"name":"quoteAddLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"name":"quoteRemoveLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermit","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityWithPermit","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"sortTokens","outputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"}],"internalType":"struct Router.route[]","name":"routes","type":"tuple[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"}],"internalType":"struct Router.route[]","name":"routes","type":"tuple[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"stable","type":"bool"}],"internalType":"struct Router.route[]","name":"routes","type":"tuple[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address","name":"tokenFrom","type":"address"},{"internalType":"address","name":"tokenTo","type":"address"},{"internalType":"bool","name":"stable","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokensSimple","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}], + "bytecode": "0x60e06040523480156200001157600080fd5b5060405162003a0638038062003a068339810160408190526200003491620000d8565b6001600160a01b038216608081905260408051631355724960e31b81529051639aab9248916004808201926020929091908290030181865afa1580156200007f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000a5919062000110565b60c0526001600160a01b031660a052506200012a565b80516001600160a01b0381168114620000d357600080fd5b919050565b60008060408385031215620000ec57600080fd5b620000f783620000bb565b91506200010760208401620000bb565b90509250929050565b6000602082840312156200012357600080fd5b5051919050565b60805160a05160c051613812620001f46000396000610f0b01526000818161013e0152818161020b0152818161091901528181610b5801528181610dbb01528181611483015281816115960152818161163601528181611e0e01528181611e4401528181611e7901528181611f230152818161205e01526120ae01526000818161042d01528181610c5001528181610ed00152818161118a0152818161129e015281816119bb01528181611b2f0152818161215d0152818161274701526127e801526138126000f3fe60806040526004361061012e5760003560e01c80635e60dab5116100ab578063a32b1fcd1161006f578063a32b1fcd146103e8578063b7e0d4c014610408578063c45a01551461041b578063d7b0e0a51461044f578063e5e31b131461046f578063f41766d81461049f57600080fd5b80635e60dab51461035557806367ffb66a146103755780637301e3c8146103885780639881fcb4146103a857806398a0fb3c146103c857600080fd5b8063448725b4116100f2578063448725b4146102655780634c1ee03e14610285578063544caa56146102a55780635a47ddc3146102e55780635e1e63251461032057600080fd5b80630dede6c41461017257806313dcfc59146101ac57806318a13086146101d95780633fc8cef3146101f95780634386e63c1461024557600080fd5b3661016d57336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461016b5761016b612bcf565b005b600080fd5b34801561017e57600080fd5b5061019261018d366004612c16565b6104bf565b604080519283526020830191909152015b60405180910390f35b3480156101b857600080fd5b506101cc6101c7366004612c9b565b6106d9565b6040516101a39190612d16565b3480156101e557600080fd5b506101cc6101f4366004612d9f565b6108ec565b34801561020557600080fd5b5061022d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101a3565b34801561025157600080fd5b50610192610260366004612e12565b610c49565b34801561027157600080fd5b50610192610280366004612e74565b610db0565b34801561029157600080fd5b5061022d6102a0366004612f1e565b610e89565b3480156102b157600080fd5b506102c56102c0366004612f69565b610f91565b604080516001600160a01b039384168152929091166020830152016101a3565b3480156102f157600080fd5b50610305610300366004612fa2565b611072565b604080519384526020840192909252908201526060016101a3565b34801561032c57600080fd5b5061034061033b366004613031565b611152565b604080519283529015156020830152016101a3565b34801561036157600080fd5b50610192610370366004612f1e565b6113a6565b6101cc610383366004613068565b61145e565b34801561039457600080fd5b506101cc6103a336600461313a565b6117cf565b3480156103b457600080fd5b506101cc6103c336600461327c565b6118a5565b3480156103d457600080fd5b506103056103e3366004613322565b611b27565b3480156103f457600080fd5b5061019261040336600461337d565b611d27565b610305610416366004613439565b611de2565b34801561042757600080fd5b5061022d7f000000000000000000000000000000000000000000000000000000000000000081565b34801561045b57600080fd5b5061019261046a366004613439565b612034565b34801561047b57600080fd5b5061048f61048a366004613494565b61213b565b60405190151581526020016101a3565b3480156104ab57600080fd5b506101cc6104ba366004612d9f565b6121d0565b60008082428110156104ec5760405162461bcd60e51b81526004016104e3906134b1565b60405180910390fd5b60006104f98c8c8c610e89565b6040516323b872dd60e01b81523360048201526001600160a01b03821660248201819052604482018c90529192506323b872dd906064016020604051808303816000875af115801561054f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061057391906134da565b61057c57600080fd5b60405163226bf2d160e21b81526001600160a01b03878116600483015260009182918416906389afcb449060240160408051808303816000875af11580156105c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ec91906134f7565b9150915060006105fc8f8f610f91565b509050806001600160a01b03168f6001600160a01b03161461061f578183610622565b82825b90975095508a8710156106775760405162461bcd60e51b815260206004820152601d60248201527f526f757465723a20494e53554646494349454e545f415f414d4f554e5400000060448201526064016104e3565b898610156106c75760405162461bcd60e51b815260206004820152601d60248201527f526f757465723a20494e53554646494349454e545f425f414d4f554e5400000060448201526064016104e3565b50505050509850989650505050505050565b606081428110156106fc5760405162461bcd60e51b81526004016104e3906134b1565b604080516001808252818301909252600091816020015b604080516060810182526000808252602080830182905292820152825260001990920191018161071357905050905087816000815181106107565761075661351b565b6020026020010151600001906001600160a01b031690816001600160a01b031681525050868160008151811061078e5761078e61351b565b6020026020010151602001906001600160a01b031690816001600160a01b03168152505085816000815181106107c6576107c661351b565b60209081029190910101519015156040909101526107e48a826118a5565b92508883600185516107f69190613547565b815181106108065761080661351b565b6020026020010151101561082c5760405162461bcd60e51b81526004016104e39061355e565b6108d4816000815181106108425761084261351b565b602002602001015160000151336108b4846000815181106108655761086561351b565b602002602001015160000151856000815181106108845761088461351b565b602002602001015160200151866000815181106108a3576108a361351b565b602002602001015160400151610e89565b866000815181106108c7576108c761351b565b60200260200101516122f5565b6108df8382876123ed565b5050979650505050505050565b6060814281101561090f5760405162461bcd60e51b81526004016104e3906134b1565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168686610946600182613547565b8181106109555761095561351b565b905060600201602001602081019061096d9190613494565b6001600160a01b0316146109935760405162461bcd60e51b81526004016104e3906135a0565b6109ef888787808060200260200160405190810160405280939291908181526020016000905b828210156109e5576109d6606083028601368190038101906135ce565b815260200190600101906109b9565b50505050506118a5565b9150868260018451610a019190613547565b81518110610a1157610a1161351b565b60200260200101511015610a375760405162461bcd60e51b81526004016104e39061355e565b610af986866000818110610a4d57610a4d61351b565b610a639260206060909202019081019150613494565b33610ae689896000818110610a7a57610a7a61351b565b610a909260206060909202019081019150613494565b8a8a6000818110610aa357610aa361351b565b9050606002016020016020810190610abb9190613494565b8b8b6000818110610ace57610ace61351b565b90506060020160400160208101906102a091906135ea565b856000815181106108c7576108c761351b565b610b56828787808060200260200160405190810160405280939291908181526020016000905b82821015610b4b57610b3c606083028601368190038101906135ce565b81526020019060010190610b1f565b5050505050306123ed565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632e1a7d4d8360018551610b949190613547565b81518110610ba457610ba461351b565b60200260200101516040518263ffffffff1660e01b8152600401610bca91815260200190565b6020604051808303816000875af1158015610be9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c0d9190613607565b50610c3e848360018551610c219190613547565b81518110610c3157610c3161351b565b6020026020010151612642565b509695505050505050565b60008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636801cc308888886040518463ffffffff1660e01b8152600401610c9e93929190613620565b602060405180830381865afa158015610cbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cdf9190613644565b90506001600160a01b038116610cfc576000809250925050610da7565b600080610d0a8989896113a6565b915091506000836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d729190613607565b905080610d7f8489613661565b610d899190613680565b955080610d968389613661565b610da09190613680565b9450505050505b94509492505050565b6000806000610de08e7f00000000000000000000000000000000000000000000000000000000000000008f610e89565b9050600087610def578c610df3565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610e2e903390309086908f908e908e908e906004016136a2565b600060405180830381600087803b158015610e4857600080fd5b505af1158015610e5c573d6000803e3d6000fd5b50505050610e6f8f8f8f8f8f8f8f612034565b809450819550505050509b509b9950505050505050505050565b6000806000610e988686610f91565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b16603482015286151560f81b604882015291935091507f000000000000000000000000000000000000000000000000000000000000000090604901604051602081830303815290604052805190602001207f0000000000000000000000000000000000000000000000000000000000000000604051602001610f6f939291906001600160f81b0319815260609390931b6bffffffffffffffffffffffff191660018401526015830191909152603582015260550190565b60408051601f1981840301815291905280516020909101209695505050505050565b600080826001600160a01b0316846001600160a01b03161415610ff65760405162461bcd60e51b815260206004820152601b60248201527f526f757465723a204944454e544943414c5f414444524553534553000000000060448201526064016104e3565b826001600160a01b0316846001600160a01b031610611016578284611019565b83835b90925090506001600160a01b03821661106b5760405162461bcd60e51b8152602060048201526014602482015273526f757465723a205a45524f5f4144445245535360601b60448201526064016104e3565b9250929050565b600080600083428110156110985760405162461bcd60e51b81526004016104e3906134b1565b6110a78d8d8d8d8d8d8d612710565b909450925060006110b98e8e8e610e89565b90506110c78e3383886122f5565b6110d38d3383876122f5565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a627842906024016020604051808303816000875af115801561111b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113f9190613607565b9250505099509950999650505050505050565b600080600061116385856001610e89565b60405163e5e31b1360e01b81526001600160a01b03828116600483015291925060009182917f00000000000000000000000000000000000000000000000000000000000000009091169063e5e31b1390602401602060405180830381865afa1580156111d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f791906134da565b15611270576040516378a051ad60e11b8152600481018990526001600160a01b03888116602483015284169063f140a35a90604401602060405180830381865afa158015611249573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061126d9190613607565b91505b61127c87876000610e89565b60405163e5e31b1360e01b81526001600160a01b0380831660048301529194507f00000000000000000000000000000000000000000000000000000000000000009091169063e5e31b1390602401602060405180830381865afa1580156112e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061130b91906134da565b15611384576040516378a051ad60e11b8152600481018990526001600160a01b03888116602483015284169063f140a35a90604401602060405180830381865afa15801561135d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113819190613607565b90505b80821161139357806000611397565b8160015b94509450505050935093915050565b60008060006113b58686610f91565b5090506000806113c6888888610e89565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015611403573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061142791906136e3565b5091509150826001600160a01b0316886001600160a01b03161461144c57808261144f565b81815b90999098509650505050505050565b606081428110156114815760405162461bcd60e51b81526004016104e3906134b1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316868660008181106114be576114be61351b565b6114d49260206060909202019081019150613494565b6001600160a01b0316146114fa5760405162461bcd60e51b81526004016104e3906135a0565b61154c348787808060200260200160405190810160405280939291908181526020016000905b828210156109e55761153d606083028601368190038101906135ce565b81526020019060010190611520565b915086826001845161155e9190613547565b8151811061156e5761156e61351b565b602002602001015110156115945760405162461bcd60e51b81526004016104e39061355e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0836000815181106115d6576115d661351b565b60200260200101516040518263ffffffff1660e01b815260040160206040518083038185885af115801561160e573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906116339190613607565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb6116cd888860008181106116795761167961351b565b61168f9260206060909202019081019150613494565b898960008181106116a2576116a261351b565b90506060020160200160208101906116ba9190613494565b8a8a6000818110610ace57610ace61351b565b846000815181106116e0576116e061351b565b60200260200101516040518363ffffffff1660e01b81526004016117199291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af1158015611738573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175c91906134da565b61176857611768612bcf565b6117c5828787808060200260200160405190810160405280939291908181526020016000905b828210156117ba576117ab606083028601368190038101906135ce565b8152602001906001019061178e565b5050505050866123ed565b5095945050505050565b606081428110156117f25760405162461bcd60e51b81526004016104e3906134b1565b611848868660008181106118085761180861351b565b61181e9260206060909202019081019150613494565b3361183589896000818110610a7a57610a7a61351b565b8a6000815181106108c7576108c761351b565b61189a878787808060200260200160405190810160405280939291908181526020016000905b828210156117ba5761188b606083028601368190038101906135ce565b8152602001906001019061186e565b509495945050505050565b60606001825110156118c95760405162461bcd60e51b81526004016104e3906135a0565b81516118d6906001613711565b67ffffffffffffffff8111156118ee576118ee6130cf565b604051908082528060200260200182016040528015611917578160200160208202803683370190505b509050828160008151811061192e5761192e61351b565b60200260200101818152505060005b8251811015611b2057600061199984838151811061195d5761195d61351b565b60200260200101516000015185848151811061197b5761197b61351b565b6020026020010151602001518685815181106108a3576108a361351b565b60405163e5e31b1360e01b81526001600160a01b0380831660048301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063e5e31b1390602401602060405180830381865afa158015611a04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a2891906134da565b15611b0d57806001600160a01b031663f140a35a848481518110611a4e57611a4e61351b565b6020026020010151868581518110611a6857611a6861351b565b6020026020010151600001516040518363ffffffff1660e01b8152600401611aa39291909182526001600160a01b0316602082015260400190565b602060405180830381865afa158015611ac0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ae49190613607565b83611af0846001613711565b81518110611b0057611b0061351b565b6020026020010181815250505b5080611b1881613729565b91505061193d565b5092915050565b6000806000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636801cc308a8a8a6040518463ffffffff1660e01b8152600401611b7d93929190613620565b602060405180830381865afa158015611b9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bbe9190613644565b9050600080806001600160a01b03841615611c4857836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c359190613607565b9050611c428c8c8c6113a6565b90935091505b82158015611c54575081155b15611c85578896508795506103e8611c74611c6f888a613661565b61298e565b611c7e9190613547565b9450611d18565b6000611c928a85856129fe565b9050888111611cda57899750955085611cd384611caf848b613661565b611cb99190613680565b84611cc4858b613661565b611cce9190613680565b612ac8565b9550611d16565b6000611ce78a85876129fe565b9850899750889050611d1285611cfd8584613661565b611d079190613680565b85611cc4868c613661565b9650505b505b50505050955095509592505050565b6000806000611d378f8f8f610e89565b9050600087611d46578c611d4a565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90611d85903390309086908f908e908e908e906004016136a2565b600060405180830381600087803b158015611d9f57600080fd5b505af1158015611db3573d6000803e3d6000fd5b5050505050611dc88f8f8f8f8f8f8f8f6104bf565b8093508194505050509c509c9a5050505050505050505050565b60008060008342811015611e085760405162461bcd60e51b81526004016104e3906134b1565b611e378b7f00000000000000000000000000000000000000000000000000000000000000008c8c348d8d612710565b90945092506000611e698c7f00000000000000000000000000000000000000000000000000000000000000008d610e89565b9050611e778c3383886122f5565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0856040518263ffffffff1660e01b815260040160206040518083038185885af1158015611ed7573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611efc9190613607565b5060405163a9059cbb60e01b81526001600160a01b038281166004830152602482018690527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af1158015611f6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9091906134da565b611f9c57611f9c612bcf565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a627842906024016020604051808303816000875af1158015611fe4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120089190613607565b92508334111561202557612025336120208634613547565b612642565b50509750975097945050505050565b60008082428110156120585760405162461bcd60e51b81526004016104e3906134b1565b6120888a7f00000000000000000000000000000000000000000000000000000000000000008b8b8b8b308b6104bf565b90935091506120988a8685612ae0565b604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d906024016020604051808303816000875af11580156120ff573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121239190613607565b5061212e8583612642565b5097509795505050505050565b60405163e5e31b1360e01b81526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063e5e31b1390602401602060405180830381865afa1580156121a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121ca91906134da565b92915050565b606081428110156121f35760405162461bcd60e51b81526004016104e3906134b1565b612245888787808060200260200160405190810160405280939291908181526020016000905b828210156109e557612236606083028601368190038101906135ce565b81526020019060010190612219565b91508682600184516122579190613547565b815181106122675761226761351b565b6020026020010151101561228d5760405162461bcd60e51b81526004016104e39061355e565b6122a386866000818110610a4d57610a4d61351b565b610c3e828787808060200260200160405190810160405280939291908181526020016000905b828210156117ba576122e6606083028601368190038101906135ce565b815260200190600101906122c9565b6000846001600160a01b03163b1161230c57600080fd5b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17905291516000928392908816916123709190613770565b6000604051808303816000865af19150503d80600081146123ad576040519150601f19603f3d011682016040523d82523d6000602084013e6123b2565b606091505b50915091508180156123dc5750805115806123dc5750808060200190518101906123dc91906134da565b6123e557600080fd5b505050505050565b60005b825181101561263c57600061243f8483815181106124105761241061351b565b60200260200101516000015185848151811061242e5761242e61351b565b602002602001015160200151610f91565b509050600085612450846001613711565b815181106124605761246061351b565b60200260200101519050600080836001600160a01b03168786815181106124895761248961351b565b6020026020010151600001516001600160a01b0316146124ab578260006124af565b6000835b915091506000600188516124c39190613547565b86106124cf5786612542565b612542886124de886001613711565b815181106124ee576124ee61351b565b602002602001015160000151898860016125089190613711565b815181106125185761251861351b565b6020026020010151602001518a8960016125329190613711565b815181106108a3576108a361351b565b90506125958887815181106125595761255961351b565b6020026020010151600001518988815181106125775761257761351b565b6020026020010151602001518a89815181106108a3576108a361351b565b6001600160a01b031663022c0d9f84848460006040519080825280601f01601f1916602001820160405280156125d2576020820181803683370190505b506040518563ffffffff1660e01b81526004016125f2949392919061378c565b600060405180830381600087803b15801561260c57600080fd5b505af1158015612620573d6000803e3d6000fd5b505050505050505050808061263490613729565b9150506123f0565b50505050565b604080516000808252602082019092526001600160a01b03841690839060405161266c9190613770565b60006040518083038185875af1925050503d80600081146126a9576040519150601f19603f3d011682016040523d82523d6000602084013e6126ae565b606091505b505090508061270b5760405162461bcd60e51b815260206004820152602360248201527f5472616e7366657248656c7065723a204554485f5452414e534645525f46414960448201526213115160ea1b60648201526084016104e3565b505050565b6000808386101561272057600080fd5b8285101561272d57600080fd5b6040516306801cc360e41b81526000906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636801cc3090612780908d908d908d90600401613620565b602060405180830381865afa15801561279d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127c19190613644565b90506001600160a01b038116612867576040516320b7f73960e21b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906382dfdce490612821908d908d908d90600401613620565b6020604051808303816000875af1158015612840573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128649190613644565b90505b6000806128758c8c8c6113a6565b91509150816000148015612887575080155b156128975788945087935061297f565b60006128a48a84846129fe565b905088811161290857868110156128fd5760405162461bcd60e51b815260206004820152601d60248201527f526f757465723a20494e53554646494349454e545f425f414d4f554e5400000060448201526064016104e3565b89955093508361297d565b60006129158a84866129fe565b90508a81111561292757612927612bcf565b888110156129775760405162461bcd60e51b815260206004820152601d60248201527f526f757465723a20494e53554646494349454e545f415f414d4f554e5400000060448201526064016104e3565b95508894505b505b50505097509795505050505050565b600060038211156129ef57508060006129a8600283613680565b6129b3906001613711565b90505b818110156129e9579050806002816129ce8186613680565b6129d89190613711565b6129e29190613680565b90506129b6565b50919050565b81156129f9575060015b919050565b6000808411612a4f5760405162461bcd60e51b815260206004820152601b60248201527f526f757465723a20494e53554646494349454e545f414d4f554e54000000000060448201526064016104e3565b600083118015612a5f5750600082115b612aab5760405162461bcd60e51b815260206004820152601e60248201527f526f757465723a20494e53554646494349454e545f4c4951554944495459000060448201526064016104e3565b82612ab68386613661565b612ac09190613680565b949350505050565b6000818310612ad75781612ad9565b825b9392505050565b6000836001600160a01b03163b11612af757600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1790529151600092839290871691612b539190613770565b6000604051808303816000865af19150503d8060008114612b90576040519150601f19603f3d011682016040523d82523d6000602084013e612b95565b606091505b5091509150818015612bbf575080511580612bbf575080806020019051810190612bbf91906134da565b612bc857600080fd5b5050505050565b634e487b7160e01b600052600160045260246000fd5b6001600160a01b0381168114612bfa57600080fd5b50565b80356129f981612be5565b8015158114612bfa57600080fd5b600080600080600080600080610100898b031215612c3357600080fd5b8835612c3e81612be5565b97506020890135612c4e81612be5565b96506040890135612c5e81612c08565b9550606089013594506080890135935060a0890135925060c0890135612c8381612be5565b8092505060e089013590509295985092959890939650565b600080600080600080600060e0888a031215612cb657600080fd5b87359650602088013595506040880135612ccf81612be5565b94506060880135612cdf81612be5565b93506080880135612cef81612c08565b925060a0880135612cff81612be5565b8092505060c0880135905092959891949750929550565b6020808252825182820181905260009190848201906040850190845b81811015612d4e57835183529284019291840191600101612d32565b50909695505050505050565b60008083601f840112612d6c57600080fd5b50813567ffffffffffffffff811115612d8457600080fd5b60208301915083602060608302850101111561106b57600080fd5b60008060008060008060a08789031215612db857600080fd5b8635955060208701359450604087013567ffffffffffffffff811115612ddd57600080fd5b612de989828a01612d5a565b9095509350506060870135612dfd81612be5565b80925050608087013590509295509295509295565b60008060008060808587031215612e2857600080fd5b8435612e3381612be5565b93506020850135612e4381612be5565b92506040850135612e5381612c08565b9396929550929360600135925050565b803560ff811681146129f957600080fd5b60008060008060008060008060008060006101608c8e031215612e9657600080fd5b8b35612ea181612be5565b9a5060208c0135612eb181612c08565b995060408c0135985060608c0135975060808c0135965060a08c0135612ed681612be5565b955060c08c0135945060e08c0135612eed81612c08565b9350612efc6101008d01612e63565b92506101208c013591506101408c013590509295989b509295989b9093969950565b600080600060608486031215612f3357600080fd5b8335612f3e81612be5565b92506020840135612f4e81612be5565b91506040840135612f5e81612c08565b809150509250925092565b60008060408385031215612f7c57600080fd5b8235612f8781612be5565b91506020830135612f9781612be5565b809150509250929050565b60008060008060008060008060006101208a8c031215612fc157600080fd5b8935612fcc81612be5565b985060208a0135612fdc81612be5565b975060408a0135612fec81612c08565b965060608a0135955060808a0135945060a08a0135935060c08a0135925060e08a013561301881612be5565b809250506101008a013590509295985092959850929598565b60008060006060848603121561304657600080fd5b83359250602084013561305881612be5565b91506040840135612f5e81612be5565b60008060008060006080868803121561308057600080fd5b85359450602086013567ffffffffffffffff81111561309e57600080fd5b6130aa88828901612d5a565b90955093505060408601356130be81612be5565b949793965091946060013592915050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561310e5761310e6130cf565b604052919050565b600067ffffffffffffffff821115613130576131306130cf565b5060051b60200190565b60008060008060006080868803121561315257600080fd5b853567ffffffffffffffff8082111561316a57600080fd5b818801915088601f83011261317e57600080fd5b8135602061319361318e83613116565b6130e5565b82815260059290921b8401810191818101908c8411156131b257600080fd5b948201945b838610156131d0578535825294820194908201906131b7565b995050890135925050808211156131e657600080fd5b506131f388828901612d5a565b90955093506130be905060408701612bfd565b60006060828403121561321857600080fd5b6040516060810181811067ffffffffffffffff8211171561323b5761323b6130cf565b604052905080823561324c81612be5565b8152602083013561325c81612be5565b6020820152604083013561326f81612c08565b6040919091015292915050565b6000806040838503121561328f57600080fd5b8235915060208084013567ffffffffffffffff8111156132ae57600080fd5b8401601f810186136132bf57600080fd5b80356132cd61318e82613116565b818152606091820283018401918482019190898411156132ec57600080fd5b938501935b83851015613312576133038a86613206565b835293840193918501916132f1565b5080955050505050509250929050565b600080600080600060a0868803121561333a57600080fd5b853561334581612be5565b9450602086013561335581612be5565b9350604086013561336581612c08565b94979396509394606081013594506080013592915050565b6000806000806000806000806000806000806101808d8f0312156133a057600080fd5b8c356133ab81612be5565b9b5060208d01356133bb81612be5565b9a5060408d01356133cb81612c08565b995060608d0135985060808d0135975060a08d0135965060c08d01356133f081612be5565b955060e08d013594506101008d013561340881612c08565b93506134176101208e01612e63565b92506101408d013591506101608d013590509295989b509295989b509295989b565b600080600080600080600060e0888a03121561345457600080fd5b873561345f81612be5565b9650602088013561346f81612c08565b955060408801359450606088013593506080880135925060a0880135612cff81612be5565b6000602082840312156134a657600080fd5b8135612ad981612be5565b6020808252600f908201526e149bdd5d195c8e8811561412549151608a1b604082015260600190565b6000602082840312156134ec57600080fd5b8151612ad981612c08565b6000806040838503121561350a57600080fd5b505080516020909101519092909150565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008282101561355957613559613531565b500390565b60208082526022908201527f526f757465723a20494e53554646494349454e545f4f55545055545f414d4f55604082015261139560f21b606082015260800190565b6020808252601490820152730a4deeae8cae47440929cac82989288bea082a8960631b604082015260600190565b6000606082840312156135e057600080fd5b612ad98383613206565b6000602082840312156135fc57600080fd5b8135612ad981612c08565b60006020828403121561361957600080fd5b5051919050565b6001600160a01b039384168152919092166020820152901515604082015260600190565b60006020828403121561365657600080fd5b8151612ad981612be5565b600081600019048311821515161561367b5761367b613531565b500290565b60008261369d57634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b6000806000606084860312156136f857600080fd5b8351925060208401519150604084015190509250925092565b6000821982111561372457613724613531565b500190565b600060001982141561373d5761373d613531565b5060010190565b60005b8381101561375f578181015183820152602001613747565b8381111561263c5750506000910152565b60008251613782818460208701613744565b9190910192915050565b84815283602082015260018060a01b038316604082015260806060820152600082518060808401526137c58160a0850160208701613744565b601f01601f19169190910160a0019594505050505056fea2646970667358221220755b85be4f0fc0117fdda889034ec422f5f182e9df9d0ba336603f0c10b74aeb64736f6c634300080b0033", + "deployedBytecode": "0x60806040526004361061012e5760003560e01c80635e60dab5116100ab578063a32b1fcd1161006f578063a32b1fcd146103e8578063b7e0d4c014610408578063c45a01551461041b578063d7b0e0a51461044f578063e5e31b131461046f578063f41766d81461049f57600080fd5b80635e60dab51461035557806367ffb66a146103755780637301e3c8146103885780639881fcb4146103a857806398a0fb3c146103c857600080fd5b8063448725b4116100f2578063448725b4146102655780634c1ee03e14610285578063544caa56146102a55780635a47ddc3146102e55780635e1e63251461032057600080fd5b80630dede6c41461017257806313dcfc59146101ac57806318a13086146101d95780633fc8cef3146101f95780634386e63c1461024557600080fd5b3661016d57336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461016b5761016b612bcf565b005b600080fd5b34801561017e57600080fd5b5061019261018d366004612c16565b6104bf565b604080519283526020830191909152015b60405180910390f35b3480156101b857600080fd5b506101cc6101c7366004612c9b565b6106d9565b6040516101a39190612d16565b3480156101e557600080fd5b506101cc6101f4366004612d9f565b6108ec565b34801561020557600080fd5b5061022d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101a3565b34801561025157600080fd5b50610192610260366004612e12565b610c49565b34801561027157600080fd5b50610192610280366004612e74565b610db0565b34801561029157600080fd5b5061022d6102a0366004612f1e565b610e89565b3480156102b157600080fd5b506102c56102c0366004612f69565b610f91565b604080516001600160a01b039384168152929091166020830152016101a3565b3480156102f157600080fd5b50610305610300366004612fa2565b611072565b604080519384526020840192909252908201526060016101a3565b34801561032c57600080fd5b5061034061033b366004613031565b611152565b604080519283529015156020830152016101a3565b34801561036157600080fd5b50610192610370366004612f1e565b6113a6565b6101cc610383366004613068565b61145e565b34801561039457600080fd5b506101cc6103a336600461313a565b6117cf565b3480156103b457600080fd5b506101cc6103c336600461327c565b6118a5565b3480156103d457600080fd5b506103056103e3366004613322565b611b27565b3480156103f457600080fd5b5061019261040336600461337d565b611d27565b610305610416366004613439565b611de2565b34801561042757600080fd5b5061022d7f000000000000000000000000000000000000000000000000000000000000000081565b34801561045b57600080fd5b5061019261046a366004613439565b612034565b34801561047b57600080fd5b5061048f61048a366004613494565b61213b565b60405190151581526020016101a3565b3480156104ab57600080fd5b506101cc6104ba366004612d9f565b6121d0565b60008082428110156104ec5760405162461bcd60e51b81526004016104e3906134b1565b60405180910390fd5b60006104f98c8c8c610e89565b6040516323b872dd60e01b81523360048201526001600160a01b03821660248201819052604482018c90529192506323b872dd906064016020604051808303816000875af115801561054f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061057391906134da565b61057c57600080fd5b60405163226bf2d160e21b81526001600160a01b03878116600483015260009182918416906389afcb449060240160408051808303816000875af11580156105c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ec91906134f7565b9150915060006105fc8f8f610f91565b509050806001600160a01b03168f6001600160a01b03161461061f578183610622565b82825b90975095508a8710156106775760405162461bcd60e51b815260206004820152601d60248201527f526f757465723a20494e53554646494349454e545f415f414d4f554e5400000060448201526064016104e3565b898610156106c75760405162461bcd60e51b815260206004820152601d60248201527f526f757465723a20494e53554646494349454e545f425f414d4f554e5400000060448201526064016104e3565b50505050509850989650505050505050565b606081428110156106fc5760405162461bcd60e51b81526004016104e3906134b1565b604080516001808252818301909252600091816020015b604080516060810182526000808252602080830182905292820152825260001990920191018161071357905050905087816000815181106107565761075661351b565b6020026020010151600001906001600160a01b031690816001600160a01b031681525050868160008151811061078e5761078e61351b565b6020026020010151602001906001600160a01b031690816001600160a01b03168152505085816000815181106107c6576107c661351b565b60209081029190910101519015156040909101526107e48a826118a5565b92508883600185516107f69190613547565b815181106108065761080661351b565b6020026020010151101561082c5760405162461bcd60e51b81526004016104e39061355e565b6108d4816000815181106108425761084261351b565b602002602001015160000151336108b4846000815181106108655761086561351b565b602002602001015160000151856000815181106108845761088461351b565b602002602001015160200151866000815181106108a3576108a361351b565b602002602001015160400151610e89565b866000815181106108c7576108c761351b565b60200260200101516122f5565b6108df8382876123ed565b5050979650505050505050565b6060814281101561090f5760405162461bcd60e51b81526004016104e3906134b1565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168686610946600182613547565b8181106109555761095561351b565b905060600201602001602081019061096d9190613494565b6001600160a01b0316146109935760405162461bcd60e51b81526004016104e3906135a0565b6109ef888787808060200260200160405190810160405280939291908181526020016000905b828210156109e5576109d6606083028601368190038101906135ce565b815260200190600101906109b9565b50505050506118a5565b9150868260018451610a019190613547565b81518110610a1157610a1161351b565b60200260200101511015610a375760405162461bcd60e51b81526004016104e39061355e565b610af986866000818110610a4d57610a4d61351b565b610a639260206060909202019081019150613494565b33610ae689896000818110610a7a57610a7a61351b565b610a909260206060909202019081019150613494565b8a8a6000818110610aa357610aa361351b565b9050606002016020016020810190610abb9190613494565b8b8b6000818110610ace57610ace61351b565b90506060020160400160208101906102a091906135ea565b856000815181106108c7576108c761351b565b610b56828787808060200260200160405190810160405280939291908181526020016000905b82821015610b4b57610b3c606083028601368190038101906135ce565b81526020019060010190610b1f565b5050505050306123ed565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632e1a7d4d8360018551610b949190613547565b81518110610ba457610ba461351b565b60200260200101516040518263ffffffff1660e01b8152600401610bca91815260200190565b6020604051808303816000875af1158015610be9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c0d9190613607565b50610c3e848360018551610c219190613547565b81518110610c3157610c3161351b565b6020026020010151612642565b509695505050505050565b60008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636801cc308888886040518463ffffffff1660e01b8152600401610c9e93929190613620565b602060405180830381865afa158015610cbb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cdf9190613644565b90506001600160a01b038116610cfc576000809250925050610da7565b600080610d0a8989896113a6565b915091506000836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d729190613607565b905080610d7f8489613661565b610d899190613680565b955080610d968389613661565b610da09190613680565b9450505050505b94509492505050565b6000806000610de08e7f00000000000000000000000000000000000000000000000000000000000000008f610e89565b9050600087610def578c610df3565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90610e2e903390309086908f908e908e908e906004016136a2565b600060405180830381600087803b158015610e4857600080fd5b505af1158015610e5c573d6000803e3d6000fd5b50505050610e6f8f8f8f8f8f8f8f612034565b809450819550505050509b509b9950505050505050505050565b6000806000610e988686610f91565b6040516bffffffffffffffffffffffff19606084811b8216602084015283901b16603482015286151560f81b604882015291935091507f000000000000000000000000000000000000000000000000000000000000000090604901604051602081830303815290604052805190602001207f0000000000000000000000000000000000000000000000000000000000000000604051602001610f6f939291906001600160f81b0319815260609390931b6bffffffffffffffffffffffff191660018401526015830191909152603582015260550190565b60408051601f1981840301815291905280516020909101209695505050505050565b600080826001600160a01b0316846001600160a01b03161415610ff65760405162461bcd60e51b815260206004820152601b60248201527f526f757465723a204944454e544943414c5f414444524553534553000000000060448201526064016104e3565b826001600160a01b0316846001600160a01b031610611016578284611019565b83835b90925090506001600160a01b03821661106b5760405162461bcd60e51b8152602060048201526014602482015273526f757465723a205a45524f5f4144445245535360601b60448201526064016104e3565b9250929050565b600080600083428110156110985760405162461bcd60e51b81526004016104e3906134b1565b6110a78d8d8d8d8d8d8d612710565b909450925060006110b98e8e8e610e89565b90506110c78e3383886122f5565b6110d38d3383876122f5565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a627842906024016020604051808303816000875af115801561111b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113f9190613607565b9250505099509950999650505050505050565b600080600061116385856001610e89565b60405163e5e31b1360e01b81526001600160a01b03828116600483015291925060009182917f00000000000000000000000000000000000000000000000000000000000000009091169063e5e31b1390602401602060405180830381865afa1580156111d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f791906134da565b15611270576040516378a051ad60e11b8152600481018990526001600160a01b03888116602483015284169063f140a35a90604401602060405180830381865afa158015611249573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061126d9190613607565b91505b61127c87876000610e89565b60405163e5e31b1360e01b81526001600160a01b0380831660048301529194507f00000000000000000000000000000000000000000000000000000000000000009091169063e5e31b1390602401602060405180830381865afa1580156112e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061130b91906134da565b15611384576040516378a051ad60e11b8152600481018990526001600160a01b03888116602483015284169063f140a35a90604401602060405180830381865afa15801561135d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113819190613607565b90505b80821161139357806000611397565b8160015b94509450505050935093915050565b60008060006113b58686610f91565b5090506000806113c6888888610e89565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa158015611403573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061142791906136e3565b5091509150826001600160a01b0316886001600160a01b03161461144c57808261144f565b81815b90999098509650505050505050565b606081428110156114815760405162461bcd60e51b81526004016104e3906134b1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316868660008181106114be576114be61351b565b6114d49260206060909202019081019150613494565b6001600160a01b0316146114fa5760405162461bcd60e51b81526004016104e3906135a0565b61154c348787808060200260200160405190810160405280939291908181526020016000905b828210156109e55761153d606083028601368190038101906135ce565b81526020019060010190611520565b915086826001845161155e9190613547565b8151811061156e5761156e61351b565b602002602001015110156115945760405162461bcd60e51b81526004016104e39061355e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0836000815181106115d6576115d661351b565b60200260200101516040518263ffffffff1660e01b815260040160206040518083038185885af115801561160e573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906116339190613607565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a9059cbb6116cd888860008181106116795761167961351b565b61168f9260206060909202019081019150613494565b898960008181106116a2576116a261351b565b90506060020160200160208101906116ba9190613494565b8a8a6000818110610ace57610ace61351b565b846000815181106116e0576116e061351b565b60200260200101516040518363ffffffff1660e01b81526004016117199291906001600160a01b03929092168252602082015260400190565b6020604051808303816000875af1158015611738573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175c91906134da565b61176857611768612bcf565b6117c5828787808060200260200160405190810160405280939291908181526020016000905b828210156117ba576117ab606083028601368190038101906135ce565b8152602001906001019061178e565b5050505050866123ed565b5095945050505050565b606081428110156117f25760405162461bcd60e51b81526004016104e3906134b1565b611848868660008181106118085761180861351b565b61181e9260206060909202019081019150613494565b3361183589896000818110610a7a57610a7a61351b565b8a6000815181106108c7576108c761351b565b61189a878787808060200260200160405190810160405280939291908181526020016000905b828210156117ba5761188b606083028601368190038101906135ce565b8152602001906001019061186e565b509495945050505050565b60606001825110156118c95760405162461bcd60e51b81526004016104e3906135a0565b81516118d6906001613711565b67ffffffffffffffff8111156118ee576118ee6130cf565b604051908082528060200260200182016040528015611917578160200160208202803683370190505b509050828160008151811061192e5761192e61351b565b60200260200101818152505060005b8251811015611b2057600061199984838151811061195d5761195d61351b565b60200260200101516000015185848151811061197b5761197b61351b565b6020026020010151602001518685815181106108a3576108a361351b565b60405163e5e31b1360e01b81526001600160a01b0380831660048301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063e5e31b1390602401602060405180830381865afa158015611a04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a2891906134da565b15611b0d57806001600160a01b031663f140a35a848481518110611a4e57611a4e61351b565b6020026020010151868581518110611a6857611a6861351b565b6020026020010151600001516040518363ffffffff1660e01b8152600401611aa39291909182526001600160a01b0316602082015260400190565b602060405180830381865afa158015611ac0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ae49190613607565b83611af0846001613711565b81518110611b0057611b0061351b565b6020026020010181815250505b5080611b1881613729565b91505061193d565b5092915050565b6000806000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636801cc308a8a8a6040518463ffffffff1660e01b8152600401611b7d93929190613620565b602060405180830381865afa158015611b9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bbe9190613644565b9050600080806001600160a01b03841615611c4857836001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c359190613607565b9050611c428c8c8c6113a6565b90935091505b82158015611c54575081155b15611c85578896508795506103e8611c74611c6f888a613661565b61298e565b611c7e9190613547565b9450611d18565b6000611c928a85856129fe565b9050888111611cda57899750955085611cd384611caf848b613661565b611cb99190613680565b84611cc4858b613661565b611cce9190613680565b612ac8565b9550611d16565b6000611ce78a85876129fe565b9850899750889050611d1285611cfd8584613661565b611d079190613680565b85611cc4868c613661565b9650505b505b50505050955095509592505050565b6000806000611d378f8f8f610e89565b9050600087611d46578c611d4a565b6000195b60405163d505accf60e01b81529091506001600160a01b0383169063d505accf90611d85903390309086908f908e908e908e906004016136a2565b600060405180830381600087803b158015611d9f57600080fd5b505af1158015611db3573d6000803e3d6000fd5b5050505050611dc88f8f8f8f8f8f8f8f6104bf565b8093508194505050509c509c9a5050505050505050505050565b60008060008342811015611e085760405162461bcd60e51b81526004016104e3906134b1565b611e378b7f00000000000000000000000000000000000000000000000000000000000000008c8c348d8d612710565b90945092506000611e698c7f00000000000000000000000000000000000000000000000000000000000000008d610e89565b9050611e778c3383886122f5565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0856040518263ffffffff1660e01b815260040160206040518083038185885af1158015611ed7573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611efc9190613607565b5060405163a9059cbb60e01b81526001600160a01b038281166004830152602482018690527f0000000000000000000000000000000000000000000000000000000000000000169063a9059cbb906044016020604051808303816000875af1158015611f6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9091906134da565b611f9c57611f9c612bcf565b6040516335313c2160e11b81526001600160a01b038881166004830152821690636a627842906024016020604051808303816000875af1158015611fe4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120089190613607565b92508334111561202557612025336120208634613547565b612642565b50509750975097945050505050565b60008082428110156120585760405162461bcd60e51b81526004016104e3906134b1565b6120888a7f00000000000000000000000000000000000000000000000000000000000000008b8b8b8b308b6104bf565b90935091506120988a8685612ae0565b604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d906024016020604051808303816000875af11580156120ff573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121239190613607565b5061212e8583612642565b5097509795505050505050565b60405163e5e31b1360e01b81526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063e5e31b1390602401602060405180830381865afa1580156121a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121ca91906134da565b92915050565b606081428110156121f35760405162461bcd60e51b81526004016104e3906134b1565b612245888787808060200260200160405190810160405280939291908181526020016000905b828210156109e557612236606083028601368190038101906135ce565b81526020019060010190612219565b91508682600184516122579190613547565b815181106122675761226761351b565b6020026020010151101561228d5760405162461bcd60e51b81526004016104e39061355e565b6122a386866000818110610a4d57610a4d61351b565b610c3e828787808060200260200160405190810160405280939291908181526020016000905b828210156117ba576122e6606083028601368190038101906135ce565b815260200190600101906122c9565b6000846001600160a01b03163b1161230c57600080fd5b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17905291516000928392908816916123709190613770565b6000604051808303816000865af19150503d80600081146123ad576040519150601f19603f3d011682016040523d82523d6000602084013e6123b2565b606091505b50915091508180156123dc5750805115806123dc5750808060200190518101906123dc91906134da565b6123e557600080fd5b505050505050565b60005b825181101561263c57600061243f8483815181106124105761241061351b565b60200260200101516000015185848151811061242e5761242e61351b565b602002602001015160200151610f91565b509050600085612450846001613711565b815181106124605761246061351b565b60200260200101519050600080836001600160a01b03168786815181106124895761248961351b565b6020026020010151600001516001600160a01b0316146124ab578260006124af565b6000835b915091506000600188516124c39190613547565b86106124cf5786612542565b612542886124de886001613711565b815181106124ee576124ee61351b565b602002602001015160000151898860016125089190613711565b815181106125185761251861351b565b6020026020010151602001518a8960016125329190613711565b815181106108a3576108a361351b565b90506125958887815181106125595761255961351b565b6020026020010151600001518988815181106125775761257761351b565b6020026020010151602001518a89815181106108a3576108a361351b565b6001600160a01b031663022c0d9f84848460006040519080825280601f01601f1916602001820160405280156125d2576020820181803683370190505b506040518563ffffffff1660e01b81526004016125f2949392919061378c565b600060405180830381600087803b15801561260c57600080fd5b505af1158015612620573d6000803e3d6000fd5b505050505050505050808061263490613729565b9150506123f0565b50505050565b604080516000808252602082019092526001600160a01b03841690839060405161266c9190613770565b60006040518083038185875af1925050503d80600081146126a9576040519150601f19603f3d011682016040523d82523d6000602084013e6126ae565b606091505b505090508061270b5760405162461bcd60e51b815260206004820152602360248201527f5472616e7366657248656c7065723a204554485f5452414e534645525f46414960448201526213115160ea1b60648201526084016104e3565b505050565b6000808386101561272057600080fd5b8285101561272d57600080fd5b6040516306801cc360e41b81526000906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636801cc3090612780908d908d908d90600401613620565b602060405180830381865afa15801561279d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127c19190613644565b90506001600160a01b038116612867576040516320b7f73960e21b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906382dfdce490612821908d908d908d90600401613620565b6020604051808303816000875af1158015612840573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128649190613644565b90505b6000806128758c8c8c6113a6565b91509150816000148015612887575080155b156128975788945087935061297f565b60006128a48a84846129fe565b905088811161290857868110156128fd5760405162461bcd60e51b815260206004820152601d60248201527f526f757465723a20494e53554646494349454e545f425f414d4f554e5400000060448201526064016104e3565b89955093508361297d565b60006129158a84866129fe565b90508a81111561292757612927612bcf565b888110156129775760405162461bcd60e51b815260206004820152601d60248201527f526f757465723a20494e53554646494349454e545f415f414d4f554e5400000060448201526064016104e3565b95508894505b505b50505097509795505050505050565b600060038211156129ef57508060006129a8600283613680565b6129b3906001613711565b90505b818110156129e9579050806002816129ce8186613680565b6129d89190613711565b6129e29190613680565b90506129b6565b50919050565b81156129f9575060015b919050565b6000808411612a4f5760405162461bcd60e51b815260206004820152601b60248201527f526f757465723a20494e53554646494349454e545f414d4f554e54000000000060448201526064016104e3565b600083118015612a5f5750600082115b612aab5760405162461bcd60e51b815260206004820152601e60248201527f526f757465723a20494e53554646494349454e545f4c4951554944495459000060448201526064016104e3565b82612ab68386613661565b612ac09190613680565b949350505050565b6000818310612ad75781612ad9565b825b9392505050565b6000836001600160a01b03163b11612af757600080fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1790529151600092839290871691612b539190613770565b6000604051808303816000865af19150503d8060008114612b90576040519150601f19603f3d011682016040523d82523d6000602084013e612b95565b606091505b5091509150818015612bbf575080511580612bbf575080806020019051810190612bbf91906134da565b612bc857600080fd5b5050505050565b634e487b7160e01b600052600160045260246000fd5b6001600160a01b0381168114612bfa57600080fd5b50565b80356129f981612be5565b8015158114612bfa57600080fd5b600080600080600080600080610100898b031215612c3357600080fd5b8835612c3e81612be5565b97506020890135612c4e81612be5565b96506040890135612c5e81612c08565b9550606089013594506080890135935060a0890135925060c0890135612c8381612be5565b8092505060e089013590509295985092959890939650565b600080600080600080600060e0888a031215612cb657600080fd5b87359650602088013595506040880135612ccf81612be5565b94506060880135612cdf81612be5565b93506080880135612cef81612c08565b925060a0880135612cff81612be5565b8092505060c0880135905092959891949750929550565b6020808252825182820181905260009190848201906040850190845b81811015612d4e57835183529284019291840191600101612d32565b50909695505050505050565b60008083601f840112612d6c57600080fd5b50813567ffffffffffffffff811115612d8457600080fd5b60208301915083602060608302850101111561106b57600080fd5b60008060008060008060a08789031215612db857600080fd5b8635955060208701359450604087013567ffffffffffffffff811115612ddd57600080fd5b612de989828a01612d5a565b9095509350506060870135612dfd81612be5565b80925050608087013590509295509295509295565b60008060008060808587031215612e2857600080fd5b8435612e3381612be5565b93506020850135612e4381612be5565b92506040850135612e5381612c08565b9396929550929360600135925050565b803560ff811681146129f957600080fd5b60008060008060008060008060008060006101608c8e031215612e9657600080fd5b8b35612ea181612be5565b9a5060208c0135612eb181612c08565b995060408c0135985060608c0135975060808c0135965060a08c0135612ed681612be5565b955060c08c0135945060e08c0135612eed81612c08565b9350612efc6101008d01612e63565b92506101208c013591506101408c013590509295989b509295989b9093969950565b600080600060608486031215612f3357600080fd5b8335612f3e81612be5565b92506020840135612f4e81612be5565b91506040840135612f5e81612c08565b809150509250925092565b60008060408385031215612f7c57600080fd5b8235612f8781612be5565b91506020830135612f9781612be5565b809150509250929050565b60008060008060008060008060006101208a8c031215612fc157600080fd5b8935612fcc81612be5565b985060208a0135612fdc81612be5565b975060408a0135612fec81612c08565b965060608a0135955060808a0135945060a08a0135935060c08a0135925060e08a013561301881612be5565b809250506101008a013590509295985092959850929598565b60008060006060848603121561304657600080fd5b83359250602084013561305881612be5565b91506040840135612f5e81612be5565b60008060008060006080868803121561308057600080fd5b85359450602086013567ffffffffffffffff81111561309e57600080fd5b6130aa88828901612d5a565b90955093505060408601356130be81612be5565b949793965091946060013592915050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561310e5761310e6130cf565b604052919050565b600067ffffffffffffffff821115613130576131306130cf565b5060051b60200190565b60008060008060006080868803121561315257600080fd5b853567ffffffffffffffff8082111561316a57600080fd5b818801915088601f83011261317e57600080fd5b8135602061319361318e83613116565b6130e5565b82815260059290921b8401810191818101908c8411156131b257600080fd5b948201945b838610156131d0578535825294820194908201906131b7565b995050890135925050808211156131e657600080fd5b506131f388828901612d5a565b90955093506130be905060408701612bfd565b60006060828403121561321857600080fd5b6040516060810181811067ffffffffffffffff8211171561323b5761323b6130cf565b604052905080823561324c81612be5565b8152602083013561325c81612be5565b6020820152604083013561326f81612c08565b6040919091015292915050565b6000806040838503121561328f57600080fd5b8235915060208084013567ffffffffffffffff8111156132ae57600080fd5b8401601f810186136132bf57600080fd5b80356132cd61318e82613116565b818152606091820283018401918482019190898411156132ec57600080fd5b938501935b83851015613312576133038a86613206565b835293840193918501916132f1565b5080955050505050509250929050565b600080600080600060a0868803121561333a57600080fd5b853561334581612be5565b9450602086013561335581612be5565b9350604086013561336581612c08565b94979396509394606081013594506080013592915050565b6000806000806000806000806000806000806101808d8f0312156133a057600080fd5b8c356133ab81612be5565b9b5060208d01356133bb81612be5565b9a5060408d01356133cb81612c08565b995060608d0135985060808d0135975060a08d0135965060c08d01356133f081612be5565b955060e08d013594506101008d013561340881612c08565b93506134176101208e01612e63565b92506101408d013591506101608d013590509295989b509295989b509295989b565b600080600080600080600060e0888a03121561345457600080fd5b873561345f81612be5565b9650602088013561346f81612c08565b955060408801359450606088013593506080880135925060a0880135612cff81612be5565b6000602082840312156134a657600080fd5b8135612ad981612be5565b6020808252600f908201526e149bdd5d195c8e8811561412549151608a1b604082015260600190565b6000602082840312156134ec57600080fd5b8151612ad981612c08565b6000806040838503121561350a57600080fd5b505080516020909101519092909150565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008282101561355957613559613531565b500390565b60208082526022908201527f526f757465723a20494e53554646494349454e545f4f55545055545f414d4f55604082015261139560f21b606082015260800190565b6020808252601490820152730a4deeae8cae47440929cac82989288bea082a8960631b604082015260600190565b6000606082840312156135e057600080fd5b612ad98383613206565b6000602082840312156135fc57600080fd5b8135612ad981612c08565b60006020828403121561361957600080fd5b5051919050565b6001600160a01b039384168152919092166020820152901515604082015260600190565b60006020828403121561365657600080fd5b8151612ad981612be5565b600081600019048311821515161561367b5761367b613531565b500290565b60008261369d57634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b0397881681529590961660208601526040850193909352606084019190915260ff16608083015260a082015260c081019190915260e00190565b6000806000606084860312156136f857600080fd5b8351925060208401519150604084015190509250925092565b6000821982111561372457613724613531565b500190565b600060001982141561373d5761373d613531565b5060010190565b60005b8381101561375f578181015183820152602001613747565b8381111561263c5750506000910152565b60008251613782818460208701613744565b9190910192915050565b84815283602082015260018060a01b038316604082015260806060820152600082518060808401526137c58160a0850160208701613744565b601f01601f19169190910160a0019594505050505056fea2646970667358221220755b85be4f0fc0117fdda889034ec422f5f182e9df9d0ba336603f0c10b74aeb64736f6c634300080b0033", + "linkReferences": {}, + "deployedLinkReferences": {} + } \ No newline at end of file diff --git a/external/contracts/velodrome/VelodromeFactory.sol b/external/contracts/velodrome/VelodromeFactory.sol new file mode 100644 index 000000000..774aeb451 --- /dev/null +++ b/external/contracts/velodrome/VelodromeFactory.sol @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.11; + +import "./VelodromePair.sol"; +import "./interfaces/IVelodromePair.sol"; +import "./interfaces/IVelodromePairCallee.sol"; +import "./interfaces/IVelodromeFactory.sol"; + +contract VelodromeFactory is IVelodromeFactory { + bool public isPaused; + address public pauser; + address public pendingPauser; + + uint256 public stableFee; + uint256 public volatileFee; + uint256 public constant MAX_FEE = 5; // 0.05% + address public feeManager; + address public pendingFeeManager; + + mapping(address => mapping(address => mapping(bool => address))) public getPair; + address[] public allPairs; + mapping(address => bool) public isPair; // simplified check if its a pair, given that `stable` flag might not be available in peripherals + + address internal _temp0; + address internal _temp1; + bool internal _temp; + + event PairCreated(address indexed token0, address indexed token1, bool stable, address pair, uint); + + constructor() { + pauser = msg.sender; + isPaused = false; + feeManager = msg.sender; + stableFee = 2; // 0.02% + volatileFee = 2; + } + + function allPairsLength() external view returns (uint) { + return allPairs.length; + } + + function setPauser(address _pauser) external { + require(msg.sender == pauser); + pendingPauser = _pauser; + } + + function acceptPauser() external { + require(msg.sender == pendingPauser); + pauser = pendingPauser; + } + + function setPause(bool _state) external { + require(msg.sender == pauser); + isPaused = _state; + } + + function setFeeManager(address _feeManager) external { + require(msg.sender == feeManager, "not fee manager"); + pendingFeeManager = _feeManager; + } + + function acceptFeeManager() external { + require(msg.sender == pendingFeeManager, "not pending fee manager"); + feeManager = pendingFeeManager; + } + + function setFee(bool _stable, uint256 _fee) external { + require(msg.sender == feeManager, "not fee manager"); + require(_fee <= MAX_FEE, "fee too high"); + require(_fee != 0, "fee must be nonzero"); + if (_stable) { + stableFee = _fee; + } else { + volatileFee = _fee; + } + } + + function getFee(bool _stable) public view returns(uint256) { + return _stable ? stableFee : volatileFee; + } + + function pairCodeHash() external pure returns (bytes32) { + return keccak256(type(VelodromePair).creationCode); + } + + function getInitializable() external view returns (address, address, bool) { + return (_temp0, _temp1, _temp); + } + + function createPair(address tokenA, address tokenB, bool stable) external returns (address pair) { + require(tokenA != tokenB, "IA"); // Pair: IDENTICAL_ADDRESSES + (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); + require(token0 != address(0), "ZA"); // Pair: ZERO_ADDRESS + require(getPair[token0][token1][stable] == address(0), "PE"); // Pair: PAIR_EXISTS - single check is sufficient + bytes32 salt = keccak256(abi.encodePacked(token0, token1, stable)); // notice salt includes stable as well, 3 parameters + (_temp0, _temp1, _temp) = (token0, token1, stable); + pair = address(new VelodromePair{salt:salt}()); + getPair[token0][token1][stable] = pair; + getPair[token1][token0][stable] = pair; // populate mapping in the reverse direction + allPairs.push(pair); + isPair[pair] = true; + emit PairCreated(token0, token1, stable, pair, allPairs.length); + } +} diff --git a/external/contracts/velodrome/VelodromePair.sol b/external/contracts/velodrome/VelodromePair.sol new file mode 100644 index 000000000..3914c31bd --- /dev/null +++ b/external/contracts/velodrome/VelodromePair.sol @@ -0,0 +1,559 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.11; + +import "./VelodromeFactory.sol"; +import "./lib/Math.sol"; +import "./interfaces/IERC20.sol"; +import "./interfaces/IVelodromePair.sol"; +import "./interfaces/IVelodromePairCallee.sol"; + +// Pair Fees contract is used as a 1:1 pair relationship to split out fees, this ensures that the curve does not need to be modified for LP shares +contract VelodromePairFees { + + address internal immutable pair; // The pair it is bonded to + address internal immutable token0; // token0 of pair, saved localy and statically for gas optimization + address internal immutable token1; // Token1 of pair, saved localy and statically for gas optimization + + constructor(address _token0, address _token1) { + pair = msg.sender; + token0 = _token0; + token1 = _token1; + } + + function _safeTransfer(address token,address to,uint256 value) internal { + require(token.code.length > 0); + (bool success, bytes memory data) = + token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value)); + require(success && (data.length == 0 || abi.decode(data, (bool)))); + } + + // Allow the pair to transfer fees to users + function claimFeesFor(address recipient, uint amount0, uint amount1) external { + require(msg.sender == pair); + if (amount0 > 0) _safeTransfer(token0, recipient, amount0); + if (amount1 > 0) _safeTransfer(token1, recipient, amount1); + } + +} + +// The base pair of pools, either stable or volatile +contract VelodromePair is IVelodromePair { + + string public name; + string public symbol; + uint8 public constant decimals = 18; + + // Used to denote stable or volatile pair, not immutable since construction happens in the initialize method for CREATE2 deterministic addresses + bool public immutable stable; + + uint public totalSupply = 0; + + mapping(address => mapping (address => uint)) public allowance; + mapping(address => uint) public balanceOf; + + bytes32 internal DOMAIN_SEPARATOR; + // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); + bytes32 internal constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; + mapping(address => uint) public nonces; + + uint internal constant MINIMUM_LIQUIDITY = 10**3; + + address public immutable token0; + address public immutable token1; + address public immutable fees; + address immutable factory; + + // Structure to capture time period obervations every 30 minutes, used for local oracles + struct Observation { + uint timestamp; + uint reserve0Cumulative; + uint reserve1Cumulative; + } + + // Capture oracle reading every 30 minutes + uint constant periodSize = 1800; + + Observation[] public observations; + + uint internal immutable decimals0; + uint internal immutable decimals1; + + uint public reserve0; + uint public reserve1; + uint public blockTimestampLast; + + uint public reserve0CumulativeLast; + uint public reserve1CumulativeLast; + + // index0 and index1 are used to accumulate fees, this is split out from normal trades to keep the swap "clean" + // this further allows LP holders to easily claim fees for tokens they have/staked + uint public index0 = 0; + uint public index1 = 0; + + // position assigned to each LP to track their current index0 & index1 vs the global position + mapping(address => uint) public supplyIndex0; + mapping(address => uint) public supplyIndex1; + + // tracks the amount of unclaimed, but claimable tokens off of fees for token0 and token1 + mapping(address => uint) public claimable0; + mapping(address => uint) public claimable1; + + event Fees(address indexed sender, uint amount0, uint amount1); + event Mint(address indexed sender, uint amount0, uint amount1); + event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); + event Swap( + address indexed sender, + uint amount0In, + uint amount1In, + uint amount0Out, + uint amount1Out, + address indexed to + ); + event Sync(uint reserve0, uint reserve1); + event Claim(address indexed sender, address indexed recipient, uint amount0, uint amount1); + + event Transfer(address indexed from, address indexed to, uint amount); + event Approval(address indexed owner, address indexed spender, uint amount); + + constructor() { + factory = msg.sender; + (address _token0, address _token1, bool _stable) = VelodromeFactory(msg.sender).getInitializable(); + (token0, token1, stable) = (_token0, _token1, _stable); + fees = address(new VelodromePairFees(_token0, _token1)); + if (_stable) { + name = string(abi.encodePacked("StableV1 AMM - ", IERC20(_token0).symbol(), "/", IERC20(_token1).symbol())); + symbol = string(abi.encodePacked("sAMM-", IERC20(_token0).symbol(), "/", IERC20(_token1).symbol())); + } else { + name = string(abi.encodePacked("VolatileV1 AMM - ", IERC20(_token0).symbol(), "/", IERC20(_token1).symbol())); + symbol = string(abi.encodePacked("vAMM-", IERC20(_token0).symbol(), "/", IERC20(_token1).symbol())); + } + + decimals0 = 10**IERC20(_token0).decimals(); + decimals1 = 10**IERC20(_token1).decimals(); + + observations.push(Observation(block.timestamp, 0, 0)); + } + + // simple re-entrancy check + uint internal _unlocked = 1; + modifier lock() { + require(_unlocked == 1); + _unlocked = 2; + _; + _unlocked = 1; + } + + function observationLength() external view returns (uint) { + return observations.length; + } + + function lastObservation() public view returns (Observation memory) { + return observations[observations.length-1]; + } + + function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1) { + return (decimals0, decimals1, reserve0, reserve1, stable, token0, token1); + } + + function tokens() external view returns (address, address) { + return (token0, token1); + } + + // claim accumulated but unclaimed fees (viewable via claimable0 and claimable1) + function claimFees() external returns (uint claimed0, uint claimed1) { + _updateFor(msg.sender); + + claimed0 = claimable0[msg.sender]; + claimed1 = claimable1[msg.sender]; + + if (claimed0 > 0 || claimed1 > 0) { + claimable0[msg.sender] = 0; + claimable1[msg.sender] = 0; + + VelodromePairFees(fees).claimFeesFor(msg.sender, claimed0, claimed1); + + emit Claim(msg.sender, msg.sender, claimed0, claimed1); + } + } + + // Accrue fees on token0 + function _update0(uint amount) internal { + _safeTransfer(token0, fees, amount); // transfer the fees out to PairFees + uint256 _ratio = amount * 1e18 / totalSupply; // 1e18 adjustment is removed during claim + if (_ratio > 0) { + index0 += _ratio; + } + emit Fees(msg.sender, amount, 0); + } + + // Accrue fees on token1 + function _update1(uint amount) internal { + _safeTransfer(token1, fees, amount); + uint256 _ratio = amount * 1e18 / totalSupply; + if (_ratio > 0) { + index1 += _ratio; + } + emit Fees(msg.sender, 0, amount); + } + + // this function MUST be called on any balance changes, otherwise can be used to infinitely claim fees + // Fees are segregated from core funds, so fees can never put liquidity at risk + function _updateFor(address recipient) internal { + uint _supplied = balanceOf[recipient]; // get LP balance of `recipient` + if (_supplied > 0) { + uint _supplyIndex0 = supplyIndex0[recipient]; // get last adjusted index0 for recipient + uint _supplyIndex1 = supplyIndex1[recipient]; + uint _index0 = index0; // get global index0 for accumulated fees + uint _index1 = index1; + supplyIndex0[recipient] = _index0; // update user current position to global position + supplyIndex1[recipient] = _index1; + uint _delta0 = _index0 - _supplyIndex0; // see if there is any difference that need to be accrued + uint _delta1 = _index1 - _supplyIndex1; + if (_delta0 > 0) { + uint _share = _supplied * _delta0 / 1e18; // add accrued difference for each supplied token + claimable0[recipient] += _share; + } + if (_delta1 > 0) { + uint _share = _supplied * _delta1 / 1e18; + claimable1[recipient] += _share; + } + } else { + supplyIndex0[recipient] = index0; // new users are set to the default global state + supplyIndex1[recipient] = index1; + } + } + + function getReserves() public view returns (uint _reserve0, uint _reserve1, uint _blockTimestampLast) { + _reserve0 = reserve0; + _reserve1 = reserve1; + _blockTimestampLast = blockTimestampLast; + } + + // update reserves and, on the first call per block, price accumulators + function _update(uint balance0, uint balance1, uint _reserve0, uint _reserve1) internal { + uint blockTimestamp = block.timestamp; + uint timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired + if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) { + reserve0CumulativeLast += _reserve0 * timeElapsed; + reserve1CumulativeLast += _reserve1 * timeElapsed; + } + + Observation memory _point = lastObservation(); + timeElapsed = blockTimestamp - _point.timestamp; // compare the last observation with current timestamp, if greater than 30 minutes, record a new event + if (timeElapsed > periodSize) { + observations.push(Observation(blockTimestamp, reserve0CumulativeLast, reserve1CumulativeLast)); + } + reserve0 = balance0; + reserve1 = balance1; + blockTimestampLast = blockTimestamp; + emit Sync(reserve0, reserve1); + } + + // produces the cumulative price using counterfactuals to save gas and avoid a call to sync. + function currentCumulativePrices() public view returns (uint reserve0Cumulative, uint reserve1Cumulative, uint blockTimestamp) { + blockTimestamp = block.timestamp; + reserve0Cumulative = reserve0CumulativeLast; + reserve1Cumulative = reserve1CumulativeLast; + + // if time has elapsed since the last update on the pair, mock the accumulated price values + (uint _reserve0, uint _reserve1, uint _blockTimestampLast) = getReserves(); + if (_blockTimestampLast != blockTimestamp) { + // subtraction overflow is desired + uint timeElapsed = blockTimestamp - _blockTimestampLast; + reserve0Cumulative += _reserve0 * timeElapsed; + reserve1Cumulative += _reserve1 * timeElapsed; + } + } + + // gives the current twap price measured from amountIn * tokenIn gives amountOut + function current(address tokenIn, uint amountIn) external view returns (uint amountOut) { + Observation memory _observation = lastObservation(); + (uint reserve0Cumulative, uint reserve1Cumulative,) = currentCumulativePrices(); + if (block.timestamp == _observation.timestamp) { + _observation = observations[observations.length-2]; + } + + uint timeElapsed = block.timestamp - _observation.timestamp; + uint _reserve0 = (reserve0Cumulative - _observation.reserve0Cumulative) / timeElapsed; + uint _reserve1 = (reserve1Cumulative - _observation.reserve1Cumulative) / timeElapsed; + amountOut = _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1); + } + + // as per `current`, however allows user configured granularity, up to the full window size + function quote(address tokenIn, uint amountIn, uint granularity) external view returns (uint amountOut) { + uint [] memory _prices = sample(tokenIn, amountIn, granularity, 1); + uint priceAverageCumulative; + for (uint i = 0; i < _prices.length; i++) { + priceAverageCumulative += _prices[i]; + } + return priceAverageCumulative / granularity; + } + + // returns a memory set of twap prices + function prices(address tokenIn, uint amountIn, uint points) external view returns (uint[] memory) { + return sample(tokenIn, amountIn, points, 1); + } + + function sample(address tokenIn, uint amountIn, uint points, uint window) public view returns (uint[] memory) { + uint[] memory _prices = new uint[](points); + + uint length = observations.length-1; + uint i = length - (points * window); + uint nextIndex = 0; + uint index = 0; + + for (; i < length; i+=window) { + nextIndex = i + window; + uint timeElapsed = observations[nextIndex].timestamp - observations[i].timestamp; + uint _reserve0 = (observations[nextIndex].reserve0Cumulative - observations[i].reserve0Cumulative) / timeElapsed; + uint _reserve1 = (observations[nextIndex].reserve1Cumulative - observations[i].reserve1Cumulative) / timeElapsed; + _prices[index] = _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1); + // index < length; length cannot overflow + unchecked { + index = index + 1; + } + } + return _prices; + } + + // this low-level function should be called by addLiquidity functions in Router.sol, which performs important safety checks + // standard uniswap v2 implementation + function mint(address to) external lock returns (uint liquidity) { + (uint _reserve0, uint _reserve1) = (reserve0, reserve1); + uint _balance0 = IERC20(token0).balanceOf(address(this)); + uint _balance1 = IERC20(token1).balanceOf(address(this)); + uint _amount0 = _balance0 - _reserve0; + uint _amount1 = _balance1 - _reserve1; + + uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee + if (_totalSupply == 0) { + liquidity = Math.sqrt(_amount0 * _amount1) - MINIMUM_LIQUIDITY; + _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens + } else { + liquidity = Math.min(_amount0 * _totalSupply / _reserve0, _amount1 * _totalSupply / _reserve1); + } + require(liquidity > 0, "ILM"); // Pair: INSUFFICIENT_LIQUIDITY_MINTED + _mint(to, liquidity); + + _update(_balance0, _balance1, _reserve0, _reserve1); + emit Mint(msg.sender, _amount0, _amount1); + } + + // this low-level function should be called from a contract which performs important safety checks + // standard uniswap v2 implementation + function burn(address to) external lock returns (uint amount0, uint amount1) { + (uint _reserve0, uint _reserve1) = (reserve0, reserve1); + (address _token0, address _token1) = (token0, token1); + uint _balance0 = IERC20(_token0).balanceOf(address(this)); + uint _balance1 = IERC20(_token1).balanceOf(address(this)); + uint _liquidity = balanceOf[address(this)]; + + uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee + amount0 = _liquidity * _balance0 / _totalSupply; // using balances ensures pro-rata distribution + amount1 = _liquidity * _balance1 / _totalSupply; // using balances ensures pro-rata distribution + require(amount0 > 0 && amount1 > 0, "ILB"); // Pair: INSUFFICIENT_LIQUIDITY_BURNED + _burn(address(this), _liquidity); + _safeTransfer(_token0, to, amount0); + _safeTransfer(_token1, to, amount1); + _balance0 = IERC20(_token0).balanceOf(address(this)); + _balance1 = IERC20(_token1).balanceOf(address(this)); + + _update(_balance0, _balance1, _reserve0, _reserve1); + emit Burn(msg.sender, amount0, amount1, to); + } + + // this low-level function should be called from a contract which performs important safety checks + function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock { + require(!VelodromeFactory(factory).isPaused()); + require(amount0Out > 0 || amount1Out > 0, "IOA"); // Pair: INSUFFICIENT_OUTPUT_AMOUNT + (uint _reserve0, uint _reserve1) = (reserve0, reserve1); + require(amount0Out < _reserve0 && amount1Out < _reserve1, "IL"); // Pair: INSUFFICIENT_LIQUIDITY + + uint _balance0; + uint _balance1; + { // scope for _token{0,1}, avoids stack too deep errors + (address _token0, address _token1) = (token0, token1); + require(to != _token0 && to != _token1, "IT"); // Pair: INVALID_TO + if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens + if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens + if (data.length > 0) IVelodromePairCallee(to).hook(msg.sender, amount0Out, amount1Out, data); // callback, used for flash loans + _balance0 = IERC20(_token0).balanceOf(address(this)); + _balance1 = IERC20(_token1).balanceOf(address(this)); + } + uint amount0In = _balance0 > _reserve0 - amount0Out ? _balance0 - (_reserve0 - amount0Out) : 0; + uint amount1In = _balance1 > _reserve1 - amount1Out ? _balance1 - (_reserve1 - amount1Out) : 0; + require(amount0In > 0 || amount1In > 0, "IIA"); // Pair: INSUFFICIENT_INPUT_AMOUNT + { // scope for reserve{0,1}Adjusted, avoids stack too deep errors + (address _token0, address _token1) = (token0, token1); + if (amount0In > 0) _update0(amount0In * VelodromeFactory(factory).getFee(stable) / 10000); // accrue fees for token0 and move them out of pool + if (amount1In > 0) _update1(amount1In * VelodromeFactory(factory).getFee(stable) / 10000); // accrue fees for token1 and move them out of pool + _balance0 = IERC20(_token0).balanceOf(address(this)); // since we removed tokens, we need to reconfirm balances, can also simply use previous balance - amountIn/ 10000, but doing balanceOf again as safety check + _balance1 = IERC20(_token1).balanceOf(address(this)); + // The curve, either x3y+y3x for stable pools, or x*y for volatile pools + require(_k(_balance0, _balance1) >= _k(_reserve0, _reserve1), "K"); // Pair: K + } + + _update(_balance0, _balance1, _reserve0, _reserve1); + emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to); + } + + // force balances to match reserves + function skim(address to) external lock { + (address _token0, address _token1) = (token0, token1); + _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)) - (reserve0)); + _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)) - (reserve1)); + } + + // force reserves to match balances + function sync() external lock { + _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1); + } + + function _f(uint x0, uint y) internal pure returns (uint) { + return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18; + } + + function _d(uint x0, uint y) internal pure returns (uint) { + return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18); + } + + function _get_y(uint x0, uint xy, uint y) internal pure returns (uint) { + for (uint i = 0; i < 255; i++) { + uint y_prev = y; + uint k = _f(x0, y); + if (k < xy) { + uint dy = (xy - k)*1e18/_d(x0, y); + y = y + dy; + } else { + uint dy = (k - xy)*1e18/_d(x0, y); + y = y - dy; + } + if (y > y_prev) { + if (y - y_prev <= 1) { + return y; + } + } else { + if (y_prev - y <= 1) { + return y; + } + } + } + return y; + } + + function getAmountOut(uint amountIn, address tokenIn) external view returns (uint) { + (uint _reserve0, uint _reserve1) = (reserve0, reserve1); + amountIn -= amountIn * VelodromeFactory(factory).getFee(stable) / 10000; // remove fee from amount received + return _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1); + } + + function _getAmountOut(uint amountIn, address tokenIn, uint _reserve0, uint _reserve1) internal view returns (uint) { + if (stable) { + uint xy = _k(_reserve0, _reserve1); + _reserve0 = _reserve0 * 1e18 / decimals0; + _reserve1 = _reserve1 * 1e18 / decimals1; + (uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0); + amountIn = tokenIn == token0 ? amountIn * 1e18 / decimals0 : amountIn * 1e18 / decimals1; + uint y = reserveB - _get_y(amountIn+reserveA, xy, reserveB); + return y * (tokenIn == token0 ? decimals1 : decimals0) / 1e18; + } else { + (uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0); + return amountIn * reserveB / (reserveA + amountIn); + } + } + + function _k(uint x, uint y) internal view returns (uint) { + if (stable) { + uint _x = x * 1e18 / decimals0; + uint _y = y * 1e18 / decimals1; + uint _a = (_x * _y) / 1e18; + uint _b = ((_x * _x) / 1e18 + (_y * _y) / 1e18); + return _a * _b / 1e18; // x3y+y3x >= k + } else { + return x * y; // xy >= k + } + } + + function _mint(address dst, uint amount) internal { + _updateFor(dst); // balances must be updated on mint/burn/transfer + totalSupply += amount; + balanceOf[dst] += amount; + emit Transfer(address(0), dst, amount); + } + + function _burn(address dst, uint amount) internal { + _updateFor(dst); + totalSupply -= amount; + balanceOf[dst] -= amount; + emit Transfer(dst, address(0), amount); + } + + function approve(address spender, uint amount) external returns (bool) { + allowance[msg.sender][spender] = amount; + + emit Approval(msg.sender, spender, amount); + return true; + } + + function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external { + require(deadline >= block.timestamp, "Pair: EXPIRED"); + DOMAIN_SEPARATOR = keccak256( + abi.encode( + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), + keccak256(bytes(name)), + keccak256(bytes("1")), + block.chainid, + address(this) + ) + ); + bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", + DOMAIN_SEPARATOR, + keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)) + ) + ); + address recoveredAddress = ecrecover(digest, v, r, s); + require(recoveredAddress != address(0) && recoveredAddress == owner, "Pair: INVALID_SIGNATURE"); + allowance[owner][spender] = value; + + emit Approval(owner, spender, value); + } + + function transfer(address dst, uint amount) external returns (bool) { + _transferTokens(msg.sender, dst, amount); + return true; + } + + function transferFrom(address src, address dst, uint amount) external returns (bool) { + address spender = msg.sender; + uint spenderAllowance = allowance[src][spender]; + + if (spender != src && spenderAllowance != type(uint).max) { + uint newAllowance = spenderAllowance - amount; + allowance[src][spender] = newAllowance; + + emit Approval(src, spender, newAllowance); + } + + _transferTokens(src, dst, amount); + return true; + } + + function _transferTokens(address src, address dst, uint amount) internal { + _updateFor(src); // update fee position for src + _updateFor(dst); // update fee position for dst + + balanceOf[src] -= amount; + balanceOf[dst] += amount; + + emit Transfer(src, dst, amount); + } + + function _safeTransfer(address token,address to,uint256 value) internal { + require(token.code.length > 0); + (bool success, bytes memory data) = + token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value)); + require(success && (data.length == 0 || abi.decode(data, (bool)))); + } +} diff --git a/external/contracts/velodrome/VelodromeRouter.sol b/external/contracts/velodrome/VelodromeRouter.sol new file mode 100644 index 000000000..5d9674005 --- /dev/null +++ b/external/contracts/velodrome/VelodromeRouter.sol @@ -0,0 +1,427 @@ +/** + *Submitted for verification at optimistic.etherscan.io on 2022-06-01 +*/ + +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.11; + +import "./lib/Math.sol"; +import "./interfaces/IVelodromeRouter.sol"; +import "./interfaces/IWETH.sol"; +import "./interfaces/IERC20.sol"; +import "./interfaces/IVelodromeFactory.sol"; +import "./interfaces/IVelodromePair.sol"; + +contract VelodromeRouter is IVelodromeRouter { + + struct route { + address from; + address to; + bool stable; + } + + address public immutable factory; + IWETH public immutable weth; + uint internal constant MINIMUM_LIQUIDITY = 10**3; + bytes32 immutable pairCodeHash; + + modifier ensure(uint deadline) { + require(deadline >= block.timestamp, "Router: EXPIRED"); + _; + } + + constructor(address _factory, address _weth) { + factory = _factory; + pairCodeHash = IVelodromeFactory(_factory).pairCodeHash(); + weth = IWETH(_weth); + } + + receive() external payable { + assert(msg.sender == address(weth)); // only accept ETH via fallback from the WETH contract + } + + function sortTokens(address tokenA, address tokenB) public pure returns (address token0, address token1) { + require(tokenA != tokenB, "Router: IDENTICAL_ADDRESSES"); + (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); + require(token0 != address(0), "Router: ZERO_ADDRESS"); + } + + // calculates the CREATE2 address for a pair without making any external calls + function pairFor(address tokenA, address tokenB, bool stable) public view returns (address pair) { + (address token0, address token1) = sortTokens(tokenA, tokenB); + pair = address(uint160(uint256(keccak256(abi.encodePacked( + hex"ff", + factory, + keccak256(abi.encodePacked(token0, token1, stable)), + pairCodeHash // init code hash + ))))); + } + + // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset + function quoteLiquidity(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) { + require(amountA > 0, "Router: INSUFFICIENT_AMOUNT"); + require(reserveA > 0 && reserveB > 0, "Router: INSUFFICIENT_LIQUIDITY"); + amountB = amountA * reserveB / reserveA; + } + + // fetches and sorts the reserves for a pair + function getReserves(address tokenA, address tokenB, bool stable) public view returns (uint reserveA, uint reserveB) { + (address token0,) = sortTokens(tokenA, tokenB); + (uint reserve0, uint reserve1,) = IVelodromePair(pairFor(tokenA, tokenB, stable)).getReserves(); + (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0); + } + + // performs chained getAmountOut calculations on any number of pairs + function getAmountOut(uint amountIn, address tokenIn, address tokenOut) external view returns (uint amount, bool stable) { + address pair = pairFor(tokenIn, tokenOut, true); + uint amountStable; + uint amountVolatile; + if (IVelodromeFactory(factory).isPair(pair)) { + amountStable = IVelodromePair(pair).getAmountOut(amountIn, tokenIn); + } + pair = pairFor(tokenIn, tokenOut, false); + if (IVelodromeFactory(factory).isPair(pair)) { + amountVolatile = IVelodromePair(pair).getAmountOut(amountIn, tokenIn); + } + return amountStable > amountVolatile ? (amountStable, true) : (amountVolatile, false); + } + + // performs chained getAmountOut calculations on any number of pairs + function getAmountsOut(uint amountIn, route[] memory routes) public view returns (uint[] memory amounts) { + require(routes.length >= 1, "Router: INVALID_PATH"); + amounts = new uint[](routes.length+1); + amounts[0] = amountIn; + for (uint i = 0; i < routes.length; i++) { + address pair = pairFor(routes[i].from, routes[i].to, routes[i].stable); + if (IVelodromeFactory(factory).isPair(pair)) { + amounts[i+1] = IVelodromePair(pair).getAmountOut(amounts[i], routes[i].from); + } + } + } + + function isPair(address pair) external view returns (bool) { + return IVelodromeFactory(factory).isPair(pair); + } + + function quoteAddLiquidity( + address tokenA, + address tokenB, + bool stable, + uint amountADesired, + uint amountBDesired + ) external view returns (uint amountA, uint amountB, uint liquidity) { + // create the pair if it doesn"t exist yet + address _pair = IVelodromeFactory(factory).getPair(tokenA, tokenB, stable); + (uint reserveA, uint reserveB) = (0,0); + uint _totalSupply = 0; + if (_pair != address(0)) { + _totalSupply = IERC20(_pair).totalSupply(); + (reserveA, reserveB) = getReserves(tokenA, tokenB, stable); + } + if (reserveA == 0 && reserveB == 0) { + (amountA, amountB) = (amountADesired, amountBDesired); + liquidity = Math.sqrt(amountA * amountB) - MINIMUM_LIQUIDITY; + } else { + + uint amountBOptimal = quoteLiquidity(amountADesired, reserveA, reserveB); + if (amountBOptimal <= amountBDesired) { + (amountA, amountB) = (amountADesired, amountBOptimal); + liquidity = Math.min(amountA * _totalSupply / reserveA, amountB * _totalSupply / reserveB); + } else { + uint amountAOptimal = quoteLiquidity(amountBDesired, reserveB, reserveA); + (amountA, amountB) = (amountAOptimal, amountBDesired); + liquidity = Math.min(amountA * _totalSupply / reserveA, amountB * _totalSupply / reserveB); + } + } + } + + function quoteRemoveLiquidity( + address tokenA, + address tokenB, + bool stable, + uint liquidity + ) external view returns (uint amountA, uint amountB) { + // create the pair if it doesn"t exist yet + address _pair = IVelodromeFactory(factory).getPair(tokenA, tokenB, stable); + + if (_pair == address(0)) { + return (0,0); + } + + (uint reserveA, uint reserveB) = getReserves(tokenA, tokenB, stable); + uint _totalSupply = IERC20(_pair).totalSupply(); + + amountA = liquidity * reserveA / _totalSupply; // using balances ensures pro-rata distribution + amountB = liquidity * reserveB / _totalSupply; // using balances ensures pro-rata distribution + + } + + function _addLiquidity( + address tokenA, + address tokenB, + bool stable, + uint amountADesired, + uint amountBDesired, + uint amountAMin, + uint amountBMin + ) internal returns (uint amountA, uint amountB) { + require(amountADesired >= amountAMin); + require(amountBDesired >= amountBMin); + // create the pair if it doesn"t exist yet + address _pair = IVelodromeFactory(factory).getPair(tokenA, tokenB, stable); + if (_pair == address(0)) { + _pair = IVelodromeFactory(factory).createPair(tokenA, tokenB, stable); + } + (uint reserveA, uint reserveB) = getReserves(tokenA, tokenB, stable); + if (reserveA == 0 && reserveB == 0) { + (amountA, amountB) = (amountADesired, amountBDesired); + } else { + uint amountBOptimal = quoteLiquidity(amountADesired, reserveA, reserveB); + if (amountBOptimal <= amountBDesired) { + require(amountBOptimal >= amountBMin, "Router: INSUFFICIENT_B_AMOUNT"); + (amountA, amountB) = (amountADesired, amountBOptimal); + } else { + uint amountAOptimal = quoteLiquidity(amountBDesired, reserveB, reserveA); + assert(amountAOptimal <= amountADesired); + require(amountAOptimal >= amountAMin, "Router: INSUFFICIENT_A_AMOUNT"); + (amountA, amountB) = (amountAOptimal, amountBDesired); + } + } + } + + function addLiquidity( + address tokenA, + address tokenB, + bool stable, + uint amountADesired, + uint amountBDesired, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) external ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) { + (amountA, amountB) = _addLiquidity(tokenA, tokenB, stable, amountADesired, amountBDesired, amountAMin, amountBMin); + address pair = pairFor(tokenA, tokenB, stable); + _safeTransferFrom(tokenA, msg.sender, pair, amountA); + _safeTransferFrom(tokenB, msg.sender, pair, amountB); + liquidity = IVelodromePair(pair).mint(to); + } + + function addLiquidityETH( + address token, + bool stable, + uint amountTokenDesired, + uint amountTokenMin, + uint amountETHMin, + address to, + uint deadline + ) external payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) { + (amountToken, amountETH) = _addLiquidity( + token, + address(weth), + stable, + amountTokenDesired, + msg.value, + amountTokenMin, + amountETHMin + ); + address pair = pairFor(token, address(weth), stable); + _safeTransferFrom(token, msg.sender, pair, amountToken); + weth.deposit{value: amountETH}(); + assert(weth.transfer(pair, amountETH)); + liquidity = IVelodromePair(pair).mint(to); + // refund dust eth, if any + if (msg.value > amountETH) _safeTransferETH(msg.sender, msg.value - amountETH); + } + + // **** REMOVE LIQUIDITY **** + function removeLiquidity( + address tokenA, + address tokenB, + bool stable, + uint liquidity, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) public ensure(deadline) returns (uint amountA, uint amountB) { + address pair = pairFor(tokenA, tokenB, stable); + require(IVelodromePair(pair).transferFrom(msg.sender, pair, liquidity)); // send liquidity to pair + (uint amount0, uint amount1) = IVelodromePair(pair).burn(to); + (address token0,) = sortTokens(tokenA, tokenB); + (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0); + require(amountA >= amountAMin, "Router: INSUFFICIENT_A_AMOUNT"); + require(amountB >= amountBMin, "Router: INSUFFICIENT_B_AMOUNT"); + } + + function removeLiquidityETH( + address token, + bool stable, + uint liquidity, + uint amountTokenMin, + uint amountETHMin, + address to, + uint deadline + ) public ensure(deadline) returns (uint amountToken, uint amountETH) { + (amountToken, amountETH) = removeLiquidity( + token, + address(weth), + stable, + liquidity, + amountTokenMin, + amountETHMin, + address(this), + deadline + ); + _safeTransfer(token, to, amountToken); + weth.withdraw(amountETH); + _safeTransferETH(to, amountETH); + } + + function removeLiquidityWithPermit( + address tokenA, + address tokenB, + bool stable, + uint liquidity, + uint amountAMin, + uint amountBMin, + address to, + uint deadline, + bool approveMax, uint8 v, bytes32 r, bytes32 s + ) external returns (uint amountA, uint amountB) { + address pair = pairFor(tokenA, tokenB, stable); + { + uint value = approveMax ? type(uint).max : liquidity; + IVelodromePair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); + } + + (amountA, amountB) = removeLiquidity(tokenA, tokenB, stable, liquidity, amountAMin, amountBMin, to, deadline); + } + + function removeLiquidityETHWithPermit( + address token, + bool stable, + uint liquidity, + uint amountTokenMin, + uint amountETHMin, + address to, + uint deadline, + bool approveMax, uint8 v, bytes32 r, bytes32 s + ) external returns (uint amountToken, uint amountETH) { + address pair = pairFor(token, address(weth), stable); + uint value = approveMax ? type(uint).max : liquidity; + IVelodromePair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); + (amountToken, amountETH) = removeLiquidityETH(token, stable, liquidity, amountTokenMin, amountETHMin, to, deadline); + } + + // **** SWAP **** + // requires the initial amount to have already been sent to the first pair + function _swap(uint[] memory amounts, route[] memory routes, address _to) internal virtual { + for (uint i = 0; i < routes.length; i++) { + (address token0,) = sortTokens(routes[i].from, routes[i].to); + uint amountOut = amounts[i + 1]; + (uint amount0Out, uint amount1Out) = routes[i].from == token0 ? (uint(0), amountOut) : (amountOut, uint(0)); + address to = i < routes.length - 1 ? pairFor(routes[i+1].from, routes[i+1].to, routes[i+1].stable) : _to; + IVelodromePair(pairFor(routes[i].from, routes[i].to, routes[i].stable)).swap( + amount0Out, amount1Out, to, new bytes(0) + ); + } + } + + function swapExactTokensForTokensSimple( + uint amountIn, + uint amountOutMin, + address tokenFrom, + address tokenTo, + bool stable, + address to, + uint deadline + ) external ensure(deadline) returns (uint[] memory amounts) { + route[] memory routes = new route[](1); + routes[0].from = tokenFrom; + routes[0].to = tokenTo; + routes[0].stable = stable; + amounts = getAmountsOut(amountIn, routes); + require(amounts[amounts.length - 1] >= amountOutMin, "Router: INSUFFICIENT_OUTPUT_AMOUNT"); + _safeTransferFrom( + routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0] + ); + _swap(amounts, routes, to); + } + + function swapExactTokensForTokens( + uint amountIn, + uint amountOutMin, + route[] calldata routes, + address to, + uint deadline + ) external ensure(deadline) returns (uint[] memory amounts) { + amounts = getAmountsOut(amountIn, routes); + require(amounts[amounts.length - 1] >= amountOutMin, "Router: INSUFFICIENT_OUTPUT_AMOUNT"); + _safeTransferFrom( + routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0] + ); + _swap(amounts, routes, to); + } + + function swapExactETHForTokens(uint amountOutMin, route[] calldata routes, address to, uint deadline) + external + payable + ensure(deadline) + returns (uint[] memory amounts) + { + require(routes[0].from == address(weth), "Router: INVALID_PATH"); + amounts = getAmountsOut(msg.value, routes); + require(amounts[amounts.length - 1] >= amountOutMin, "Router: INSUFFICIENT_OUTPUT_AMOUNT"); + weth.deposit{value: amounts[0]}(); + assert(weth.transfer(pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0])); + _swap(amounts, routes, to); + } + + function swapExactTokensForETH(uint amountIn, uint amountOutMin, route[] calldata routes, address to, uint deadline) + external + ensure(deadline) + returns (uint[] memory amounts) + { + require(routes[routes.length - 1].to == address(weth), "Router: INVALID_PATH"); + amounts = getAmountsOut(amountIn, routes); + require(amounts[amounts.length - 1] >= amountOutMin, "Router: INSUFFICIENT_OUTPUT_AMOUNT"); + _safeTransferFrom( + routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0] + ); + _swap(amounts, routes, address(this)); + weth.withdraw(amounts[amounts.length - 1]); + _safeTransferETH(to, amounts[amounts.length - 1]); + } + + function UNSAFE_swapExactTokensForTokens( + uint[] memory amounts, + route[] calldata routes, + address to, + uint deadline + ) external ensure(deadline) returns (uint[] memory) { + _safeTransferFrom(routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]); + _swap(amounts, routes, to); + return amounts; + } + + function _safeTransferETH(address to, uint value) internal { + (bool success,) = to.call{value:value}(new bytes(0)); + require(success, "TransferHelper: ETH_TRANSFER_FAILED"); + } + + function _safeTransfer(address token, address to, uint256 value) internal { + require(token.code.length > 0); + (bool success, bytes memory data) = + token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value)); + require(success && (data.length == 0 || abi.decode(data, (bool)))); + } + + function _safeTransferFrom(address token, address from, address to, uint256 value) internal { + require(token.code.length > 0); + (bool success, bytes memory data) = + token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value)); + require(success && (data.length == 0 || abi.decode(data, (bool)))); + } +} diff --git a/external/contracts/velodrome/interfaces/IERC20.sol b/external/contracts/velodrome/interfaces/IERC20.sol new file mode 100644 index 000000000..7b20707b5 --- /dev/null +++ b/external/contracts/velodrome/interfaces/IERC20.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.11; + +interface IERC20 { + function totalSupply() external view returns (uint256); + function transfer(address recipient, uint amount) external returns (bool); + function decimals() external view returns (uint8); + function symbol() external view returns (string memory); + function balanceOf(address) external view returns (uint); + function transferFrom(address sender, address recipient, uint amount) external returns (bool); + function allowance(address owner, address spender) external view returns (uint); + function approve(address spender, uint value) external returns (bool); + + event Transfer(address indexed from, address indexed to, uint value); + event Approval(address indexed owner, address indexed spender, uint value); +} diff --git a/external/contracts/velodrome/interfaces/IVelodromeFactory.sol b/external/contracts/velodrome/interfaces/IVelodromeFactory.sol new file mode 100644 index 000000000..222b6d4b3 --- /dev/null +++ b/external/contracts/velodrome/interfaces/IVelodromeFactory.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.11; + +interface IVelodromeFactory { + function allPairsLength() external view returns (uint); + function isPair(address pair) external view returns (bool); + function pairCodeHash() external pure returns (bytes32); + function getPair(address tokenA, address token, bool stable) external view returns (address); + function createPair(address tokenA, address tokenB, bool stable) external returns (address pair); +} diff --git a/external/contracts/velodrome/interfaces/IVelodromePair.sol b/external/contracts/velodrome/interfaces/IVelodromePair.sol new file mode 100644 index 000000000..403164f73 --- /dev/null +++ b/external/contracts/velodrome/interfaces/IVelodromePair.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.11; + +interface IVelodromePair { + function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1); + function claimFees() external returns (uint, uint); + function tokens() external returns (address, address); + function transferFrom(address src, address dst, uint amount) external returns (bool); + function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; + function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; + function burn(address to) external returns (uint amount0, uint amount1); + function mint(address to) external returns (uint liquidity); + function getReserves() external view returns (uint _reserve0, uint _reserve1, uint _blockTimestampLast); + function getAmountOut(uint, address) external view returns (uint); +} diff --git a/external/contracts/velodrome/interfaces/IVelodromePairCallee.sol b/external/contracts/velodrome/interfaces/IVelodromePairCallee.sol new file mode 100644 index 000000000..2dd40498d --- /dev/null +++ b/external/contracts/velodrome/interfaces/IVelodromePairCallee.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.11; + +interface IVelodromePairCallee { + function hook(address sender, uint amount0, uint amount1, bytes calldata data) external; +} diff --git a/external/contracts/velodrome/interfaces/IVelodromeRouter.sol b/external/contracts/velodrome/interfaces/IVelodromeRouter.sol new file mode 100644 index 000000000..f4cc64bd3 --- /dev/null +++ b/external/contracts/velodrome/interfaces/IVelodromeRouter.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.11; + +interface IVelodromeRouter { + function pairFor(address tokenA, address tokenB, bool stable) external view returns (address pair); +} diff --git a/external/contracts/velodrome/interfaces/IWETH.sol b/external/contracts/velodrome/interfaces/IWETH.sol new file mode 100644 index 000000000..ea9475654 --- /dev/null +++ b/external/contracts/velodrome/interfaces/IWETH.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.11; + +interface IWETH { + function deposit() external payable returns (uint); + function transfer(address to, uint value) external returns (bool); + function withdraw(uint) external returns (uint); +} diff --git a/external/contracts/velodrome/lib/Math.sol b/external/contracts/velodrome/lib/Math.sol new file mode 100644 index 000000000..18b75bbdb --- /dev/null +++ b/external/contracts/velodrome/lib/Math.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.11; + +library Math { + function max(uint a, uint b) internal pure returns (uint) { + return a >= b ? a : b; + } + function min(uint a, uint b) internal pure returns (uint) { + return a < b ? a : b; + } + 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; + } + } + function cbrt(uint256 n) internal pure returns (uint256) { unchecked { + uint256 x = 0; + for (uint256 y = 1 << 255; y > 0; y >>= 3) { + x <<= 1; + uint256 z = 3 * x * (x + 1) + 1; + if (n / y >= z) { + n -= y * z; + x += 1; + } + } + return x; + }} +} diff --git a/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts b/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts index 5a46ebcd7..0f67c881e 100644 --- a/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts +++ b/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts @@ -1,6 +1,6 @@ import "module-alias/register"; -import { BigNumber, ethers } from "ethers"; +import { BigNumber } from "ethers"; import { utils } from "ethers"; import { Address, Bytes } from "@utils/types"; @@ -15,18 +15,19 @@ import { getSystemFixture, getWaffleExpect, getLastBlockTimestamp, + getVelodromeFixture, } from "@utils/test/index"; import { SystemFixture } from "@utils/fixtures"; -import { IVelodromeRouter__factory } from "@typechain/factories/IVelodromeRouter__factory"; +import { VelodromeFixture } from "@utils/fixtures/velodromeFixture"; +import { ethers } from "hardhat"; const expect = getWaffleExpect(); describe("VelodromeExchangeAdapter", () => { - const velodromRouterAddress = ethers.Wallet.createRandom().address; - let owner: Account; let mockSetToken: Account; let deployer: DeployHelper; let setup: SystemFixture; + let velodromeSetup: VelodromeFixture; let velodromeExchangeAdapter: VelodromeExchangeAdapter; before(async () => { @@ -36,8 +37,11 @@ describe("VelodromeExchangeAdapter", () => { setup = getSystemFixture(owner.address); await setup.initialize(); + velodromeSetup = getVelodromeFixture(owner.address); + await velodromeSetup.initialize(owner, setup.weth.address); + velodromeExchangeAdapter = await deployer.adapters.deployVelodromeExchangeAdapter( - velodromRouterAddress, + velodromeSetup.router.address, ); }); @@ -47,7 +51,7 @@ describe("VelodromeExchangeAdapter", () => { let subjectVelodromeRouter: Address; beforeEach(async () => { - subjectVelodromeRouter = velodromRouterAddress; + subjectVelodromeRouter = velodromeSetup.router.address; }); async function subject(): Promise { @@ -58,7 +62,7 @@ describe("VelodromeExchangeAdapter", () => { const deployedVelodromeV2ExchangeAdapter = await subject(); const actualRouterAddress = await deployedVelodromeV2ExchangeAdapter.router(); - expect(actualRouterAddress).to.eq(velodromRouterAddress); + expect(actualRouterAddress).to.eq(velodromeSetup.router.address); }); }); @@ -70,7 +74,7 @@ describe("VelodromeExchangeAdapter", () => { it("should return the correct spender address", async () => { const spender = await subject(); - expect(spender).to.eq(velodromRouterAddress); + expect(spender).to.eq(velodromeSetup.router.address); }); }); @@ -111,37 +115,73 @@ describe("VelodromeExchangeAdapter", () => { ); } - it("should return the correct trade calldata", async () => { - subjectData = await velodromeExchangeAdapter.generateDataParam([ - { - from: sourceAddress, - to: destinationAddress, - stable: false, - }, - ]); - const calldata = await subject(); - const callTimestamp = await getLastBlockTimestamp(); - const expectedCallData = IVelodromeRouter__factory.createInterface().encodeFunctionData( - "swapExactTokensForTokens", - [ - sourceQuantity, - destinationQuantity, - [{ from: sourceAddress, to: destinationAddress, stable: false }], - subjectMockSetToken, - callTimestamp, - ], - ); - expect(JSON.stringify(calldata)).to.eq( - JSON.stringify([velodromRouterAddress, ZERO, expectedCallData]), - ); + describe("wbtc -> dai", async () => { + it("should return the correct data param", async () => { + subjectData = await velodromeExchangeAdapter.generateDataParam([ + { + from: sourceAddress, + to: destinationAddress, + stable: false, + }, + ]); + expect(subjectData).to.eq( + ethers.utils.defaultAbiCoder.encode( + ["tuple(address,address,bool)[]"], + [[[sourceAddress, destinationAddress, false]]], + ), + ); + }); + + it("should return the correct trade calldata", async () => { + subjectData = await velodromeExchangeAdapter.generateDataParam([ + { + from: sourceAddress, + to: destinationAddress, + stable: false, + }, + ]); + const calldata = await subject(); + const callTimestamp = await getLastBlockTimestamp(); + const expectedCallData = velodromeSetup.router.interface.encodeFunctionData( + "swapExactTokensForTokens", + [ + sourceQuantity, + destinationQuantity, + [{ from: sourceAddress, to: destinationAddress, stable: false }], + subjectMockSetToken, + callTimestamp, + ], + ); + expect(JSON.stringify(calldata)).to.eq( + JSON.stringify([velodromeSetup.router.address, ZERO, expectedCallData]), + ); + }); }); - describe("when passed in custom path to trade data", async () => { + describe("wbtc -> weth -> dai", async () => { beforeEach(async () => { const path = [sourceAddress, setup.weth.address, destinationAddress]; subjectData = utils.defaultAbiCoder.encode(["address[]"], [path]); }); + it("should return the correct data param", async () => { + subjectData = await velodromeExchangeAdapter.generateDataParam([ + { from: sourceAddress, to: setup.weth.address, stable: false }, + { from: setup.weth.address, to: destinationAddress, stable: false }, + ]); + expect(subjectData).to.eq( + ethers.utils.defaultAbiCoder.encode( + ["tuple(address,address,bool)[]"], + [ + [ + [sourceAddress, setup.weth.address, false], + [setup.weth.address, destinationAddress, false], + ], + ], + ), + ); + }); + it("should return the correct trade calldata", async () => { subjectData = await velodromeExchangeAdapter.generateDataParam([ { from: sourceAddress, to: setup.weth.address, stable: false }, @@ -149,7 +189,7 @@ describe("VelodromeExchangeAdapter", () => { ]); const calldata = await subject(); const callTimestamp = await getLastBlockTimestamp(); - const expectedCallData = IVelodromeRouter__factory.createInterface().encodeFunctionData( + const expectedCallData = velodromeSetup.router.interface.encodeFunctionData( "swapExactTokensForTokens", [ sourceQuantity, @@ -163,7 +203,7 @@ describe("VelodromeExchangeAdapter", () => { ], ); expect(JSON.stringify(calldata)).to.eq( - JSON.stringify([velodromRouterAddress, ZERO, expectedCallData]), + JSON.stringify([velodromeSetup.router.address, ZERO, expectedCallData]), ); }); }); diff --git a/utils/contracts/velodrome.ts b/utils/contracts/velodrome.ts new file mode 100644 index 000000000..88d3a722d --- /dev/null +++ b/utils/contracts/velodrome.ts @@ -0,0 +1,8 @@ +// External Uniswap Contracts +export { StakingRewards } from "../../typechain/StakingRewards"; +export { Uni } from "../../typechain/Uni"; +export { UniswapGovernorAlpha } from "../../typechain/UniswapGovernorAlpha"; +export { UniswapTimelock } from "../../typechain/UniswapTimelock"; +export { VelodromeFactory } from "../../typechain/VelodromeFactory"; +export { UniswapV2Pair } from "../../typechain/UniswapV2Pair"; +export { UniswapV2Router02 } from "../../typechain/UniswapV2Router02"; \ No newline at end of file diff --git a/utils/deploys/dependencies.ts b/utils/deploys/dependencies.ts index 1da9147c3..40fac66c9 100644 --- a/utils/deploys/dependencies.ts +++ b/utils/deploys/dependencies.ts @@ -124,6 +124,11 @@ export default { 42: "0x4e67bf5bD28Dd4b570FBAFe11D0633eCbA2754Ec", }, + VELODROME_ROUTER: { + 10: "0xa132DAB612dB5cB9fC9Ac426A0Cc215A3423F9c9", + 42: "", + }, + // Governance AAVE_PROTO_GOVERNANCE: { diff --git a/utils/deploys/deployExternal.ts b/utils/deploys/deployExternal.ts index 0ef99cfbd..cd91cf123 100644 --- a/utils/deploys/deployExternal.ts +++ b/utils/deploys/deployExternal.ts @@ -202,6 +202,12 @@ import { PerpV2ClearingHouseConfig__factory } from "../../typechain/factories/Pe import { PerpV2InsuranceFund__factory } from "../../typechain/factories/PerpV2InsuranceFund__factory"; import { PerpV2AccountBalance__factory } from "../../typechain/factories/PerpV2AccountBalance__factory"; import { PerpV2Exchange__factory } from "../../typechain/factories/PerpV2Exchange__factory"; +import { VelodromeFactory } from "@typechain/VelodromeFactory"; +import { VelodromeRouter } from "@typechain/VelodromeRouter"; +import { VelodromePair } from "@typechain/VelodromePair"; +import { VelodromeFactory__factory } from "@typechain/factories/VelodromeFactory__factory"; +import { VelodromeRouter__factory } from "@typechain/factories/VelodromeRouter__factory"; +import { VelodromePair__factory } from "@typechain/factories/VelodromePair__factory"; export default class DeployExternalContracts { @@ -740,4 +746,21 @@ export default class DeployExternalContracts { public async getVToken(token: Address): Promise { return await new PerpV2BaseToken__factory(this._deployerSigner).attach(token); } + + // Velodrome + public async deployVelodromeFactory(): Promise { + return await new VelodromeFactory__factory(this._deployerSigner).deploy(); + } + + public async deployVelodromeRouter(_factory: Address, _weth: Address): Promise { + return await new VelodromeRouter__factory(this._deployerSigner).deploy(_factory, _weth); + } + + public async deployVelodromePair(): Promise { + return await new VelodromePair__factory(this._deployerSigner).deploy(); + } + + public getForkedVelodromeRouter(_mainnetRouter: Address): VelodromeRouter { + return VelodromeRouter__factory.connect(_mainnetRouter, this._deployerSigner); + } } diff --git a/utils/fixtures/velodromeFixture.ts b/utils/fixtures/velodromeFixture.ts new file mode 100644 index 000000000..4989f7f8c --- /dev/null +++ b/utils/fixtures/velodromeFixture.ts @@ -0,0 +1,66 @@ +import DeployHelper from "../deploys"; +import { Signer, providers, BigNumber } from "ethers"; +import { Address } from "../types"; +import { Account } from "../test/types"; + +import dependencies from "../deploys/dependencies"; +import { Uni } from "@typechain/Uni"; +import { VelodromeFactory } from "@typechain/VelodromeFactory"; +import { VelodromePair } from "@typechain/VelodromePair"; +import { VelodromeRouter } from "@typechain/VelodromeRouter"; +import { VelodromePair__factory } from "@typechain/factories/VelodromePair__factory"; + +export class VelodromeFixture { + private _deployer: DeployHelper; + private _provider: providers.Web3Provider | providers.JsonRpcProvider; + private _ownerSigner: Signer; + + public owner: Account; + public uni: Uni; + public factory: VelodromeFactory; + public pair: VelodromePair; + public router: VelodromeRouter; + + public uniWethPool: VelodromePair; + + constructor(provider: providers.Web3Provider | providers.JsonRpcProvider, ownerAddress: Address) { + this._ownerSigner = provider.getSigner(ownerAddress); + this._provider = provider; + this._deployer = new DeployHelper(this._ownerSigner); + } + + public async initialize(_owner: Account, _weth: Address): Promise { + this.owner = _owner; + this.factory = await this._deployer.external.deployVelodromeFactory(); + this.router = await this._deployer.external.deployVelodromeRouter(this.factory.address, _weth); + + const lastBlock = await this._provider.getBlock("latest"); + this.uni = await this._deployer.external.deployUni( + this.owner.address, + this.owner.address, + BigNumber.from(lastBlock.timestamp).add(2), + ); + + this.uniWethPool = await this.createNewPair(_weth, this.uni.address, false); + } + + public async createNewPair( + _tokenOne: Address, + _tokenTwo: Address, + stable: boolean, + ): Promise { + await this.factory.createPair(_tokenOne, _tokenTwo, stable); + const poolAddress = await this.factory.allPairs((await this.factory.allPairsLength()).sub(1)); + return await new VelodromePair__factory(this._ownerSigner).attach(poolAddress); + } + + public getTokenOrder(_tokenOne: Address, _tokenTwo: Address): [Address, Address] { + return _tokenOne.toLowerCase() < _tokenTwo.toLowerCase() + ? [_tokenOne, _tokenTwo] + : [_tokenTwo, _tokenOne]; + } + + public getForkedVelodromeRouter(): VelodromeRouter { + return this._deployer.external.getForkedVelodromeRouter(dependencies.VELODROME_ROUTER[10]); + } +} diff --git a/utils/test/index.ts b/utils/test/index.ts index 3cb59708d..50cb9d2d3 100644 --- a/utils/test/index.ts +++ b/utils/test/index.ts @@ -15,6 +15,7 @@ import { PerpV2Fixture } from "../fixtures"; import { Blockchain, ProtocolUtils } from "../common"; +import { VelodromeFixture } from "@utils/fixtures/velodromeFixture"; // Hardhat-Provider Aware Exports const provider = ethers.provider; @@ -27,6 +28,7 @@ export const getCurveFixture = (ownerAddress: Address) => new CurveFixture(provi export const getCompoundFixture = (ownerAddress: Address) => new CompoundFixture(provider, ownerAddress); export const getKyberV3DMMFixture = (ownerAddress: Address) => new KyberV3DMMFixture(provider, ownerAddress); export const getUniswapFixture = (ownerAddress: Address) => new UniswapFixture(provider, ownerAddress); +export const getVelodromeFixture = (ownerAddress: Address) => new VelodromeFixture(provider, ownerAddress); export const getYearnFixture = (ownerAddress: Address) => new YearnFixture(provider, ownerAddress); export const getUniswapV3Fixture = (ownerAddress: Address) => new UniswapV3Fixture(provider, ownerAddress); export const getPerpV2Fixture = (ownerAddress: Address) => new PerpV2Fixture(provider, ownerAddress); From b07c422193e81d21ca4258a4fed685089f640619 Mon Sep 17 00:00:00 2001 From: deephil0226 Date: Wed, 29 Jun 2022 07:51:43 -0400 Subject: [PATCH 04/10] skip velodrome exchange trade module integration test --- ...eModule.spec.ts => velodromeExchangeTradeModule.spec.draft.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/integration/{velodromeExchangeTradeModule.spec.ts => velodromeExchangeTradeModule.spec.draft.ts} (100%) diff --git a/test/integration/velodromeExchangeTradeModule.spec.ts b/test/integration/velodromeExchangeTradeModule.spec.draft.ts similarity index 100% rename from test/integration/velodromeExchangeTradeModule.spec.ts rename to test/integration/velodromeExchangeTradeModule.spec.draft.ts From a8b7f722e9a2d4a6dcd941f79aaa6232d1217773 Mon Sep 17 00:00:00 2001 From: deephil0226 Date: Wed, 29 Jun 2022 07:59:15 -0400 Subject: [PATCH 05/10] fork optimism for velodrome integration test --- ...s => velodromeExchangeTradeModule.spec.ts} | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) rename test/integration/{velodromeExchangeTradeModule.spec.draft.ts => velodromeExchangeTradeModule.spec.ts} (94%) diff --git a/test/integration/velodromeExchangeTradeModule.spec.draft.ts b/test/integration/velodromeExchangeTradeModule.spec.ts similarity index 94% rename from test/integration/velodromeExchangeTradeModule.spec.draft.ts rename to test/integration/velodromeExchangeTradeModule.spec.ts index 56a015288..ac0c195bc 100644 --- a/test/integration/velodromeExchangeTradeModule.spec.draft.ts +++ b/test/integration/velodromeExchangeTradeModule.spec.ts @@ -36,6 +36,18 @@ describe("Velodrome TradeModule Integration [@optimism]", () => { let tradeModule: TradeModule; before(async () => { + await network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + jsonRpcUrl: `https://opt-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_TOKEN}`, + blockNumber: 13454300, + }, + }, + ], + }); + [owner, manager] = await getAccounts(); deployer = new DeployHelper(owner.wallet); @@ -61,6 +73,13 @@ describe("Velodrome TradeModule Integration [@optimism]", () => { ); }); + after(async () => { + await network.provider.request({ + method: "hardhat_reset", + params: [], + }); + }); + describe("#trade", function () { let sourceToken: IERC20; let destinationToken: IERC20; From e8a83a5406dca8cb01963b2e0786522a761731e8 Mon Sep 17 00:00:00 2001 From: deephil0226 Date: Wed, 29 Jun 2022 08:10:00 -0400 Subject: [PATCH 06/10] use deadline as parameter --- .../exchange/VelodromeExchangeAdapter.sol | 14 ++-- .../velodromeExchangeTradeModule.spec.ts | 17 +++-- .../exchange/velodromeExchangeAdapter.spec.ts | 67 +++++++++++-------- 3 files changed, 58 insertions(+), 40 deletions(-) diff --git a/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol b/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol index 8e49d227a..7897e01b5 100644 --- a/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol +++ b/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol @@ -74,8 +74,9 @@ contract VelodromeExchangeAdapter { returns (address, uint256, bytes memory) { ( - IVelodromeRouter.route[] memory routes - ) = abi.decode(_data, (IVelodromeRouter.route[])); + IVelodromeRouter.route[] memory routes, + uint256 deadline + ) = abi.decode(_data, (IVelodromeRouter.route[], uint256)); require(routes.length > 0, "empty routes"); require(_sourceToken == routes[0].from, "Source token path mismatch"); @@ -87,7 +88,7 @@ contract VelodromeExchangeAdapter { _minDestinationQuantity, routes, _destinationAddress, - block.timestamp + deadline ); return (router, 0, callData); } @@ -109,14 +110,15 @@ contract VelodromeExchangeAdapter { * Generate data parameter to be passed to `getTradeCallData`. Returns encoded trade routes. * * @param _routes array of routes for Velodrome Router - * @return bytes Data parameter to be passed to `getTradeCallData` + * @param _deadline block timestamp of trade deadline + * @return bytes Data parameter to be passed to `getTradeCallData` */ - function generateDataParam(IVelodromeRouter.route[] calldata _routes) + function generateDataParam(IVelodromeRouter.route[] calldata _routes, uint256 _deadline) external pure returns (bytes memory) { require(_routes.length > 0, "empty routes"); - return abi.encode(_routes); + return abi.encode(_routes, _deadline); } } \ No newline at end of file diff --git a/test/integration/velodromeExchangeTradeModule.spec.ts b/test/integration/velodromeExchangeTradeModule.spec.ts index ac0c195bc..83e1b056f 100644 --- a/test/integration/velodromeExchangeTradeModule.spec.ts +++ b/test/integration/velodromeExchangeTradeModule.spec.ts @@ -170,13 +170,16 @@ describe("Velodrome TradeModule Integration [@optimism]", () => { subjectMinDestinationQuantity = destinationTokenQuantity.sub(usdc(1)); subjectAdapterName = velodromeAdapterName; - subjectData = await velodromeExchangeAdapter.generateDataParam([ - { - from: subjectSourceToken, - to: subjectDestinationToken, - stable: false, - }, - ]); + subjectData = await velodromeExchangeAdapter.generateDataParam( + [ + { + from: subjectSourceToken, + to: subjectDestinationToken, + stable: false, + }, + ], + ethers.constants.MaxUint256, + ); subjectCaller = manager; }); diff --git a/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts b/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts index 0f67c881e..79a84cdfc 100644 --- a/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts +++ b/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts @@ -117,31 +117,37 @@ describe("VelodromeExchangeAdapter", () => { describe("wbtc -> dai", async () => { it("should return the correct data param", async () => { - subjectData = await velodromeExchangeAdapter.generateDataParam([ - { - from: sourceAddress, - to: destinationAddress, - stable: false, - }, - ]); + subjectData = await velodromeExchangeAdapter.generateDataParam( + [ + { + from: sourceAddress, + to: destinationAddress, + stable: false, + }, + ], + ethers.constants.MaxUint256, + ); expect(subjectData).to.eq( ethers.utils.defaultAbiCoder.encode( - ["tuple(address,address,bool)[]"], - [[[sourceAddress, destinationAddress, false]]], + ["tuple(address,address,bool)[]", "uint256"], + [[[sourceAddress, destinationAddress, false]], ethers.constants.MaxUint256], ), ); }); it("should return the correct trade calldata", async () => { - subjectData = await velodromeExchangeAdapter.generateDataParam([ - { - from: sourceAddress, - to: destinationAddress, - stable: false, - }, - ]); - const calldata = await subject(); const callTimestamp = await getLastBlockTimestamp(); + subjectData = await velodromeExchangeAdapter.generateDataParam( + [ + { + from: sourceAddress, + to: destinationAddress, + stable: false, + }, + ], + callTimestamp, + ); + const calldata = await subject(); const expectedCallData = velodromeSetup.router.interface.encodeFunctionData( "swapExactTokensForTokens", [ @@ -165,30 +171,37 @@ describe("VelodromeExchangeAdapter", () => { }); it("should return the correct data param", async () => { - subjectData = await velodromeExchangeAdapter.generateDataParam([ - { from: sourceAddress, to: setup.weth.address, stable: false }, - { from: setup.weth.address, to: destinationAddress, stable: false }, - ]); + subjectData = await velodromeExchangeAdapter.generateDataParam( + [ + { from: sourceAddress, to: setup.weth.address, stable: false }, + { from: setup.weth.address, to: destinationAddress, stable: false }, + ], + ethers.constants.MaxUint256, + ); expect(subjectData).to.eq( ethers.utils.defaultAbiCoder.encode( - ["tuple(address,address,bool)[]"], + ["tuple(address,address,bool)[]", "uint256"], [ [ [sourceAddress, setup.weth.address, false], [setup.weth.address, destinationAddress, false], ], + ethers.constants.MaxUint256, ], ), ); }); it("should return the correct trade calldata", async () => { - subjectData = await velodromeExchangeAdapter.generateDataParam([ - { from: sourceAddress, to: setup.weth.address, stable: false }, - { from: setup.weth.address, to: destinationAddress, stable: false }, - ]); - const calldata = await subject(); const callTimestamp = await getLastBlockTimestamp(); + subjectData = await velodromeExchangeAdapter.generateDataParam( + [ + { from: sourceAddress, to: setup.weth.address, stable: false }, + { from: setup.weth.address, to: destinationAddress, stable: false }, + ], + callTimestamp, + ); + const calldata = await subject(); const expectedCallData = velodromeSetup.router.interface.encodeFunctionData( "swapExactTokensForTokens", [ From d2e52ccf9990e3b3885a0a84bfd15011ae83cd51 Mon Sep 17 00:00:00 2001 From: deephil0226 Date: Wed, 29 Jun 2022 13:57:50 -0400 Subject: [PATCH 07/10] update imports from typechain --- ...ts => velodromeExchangeTradeModule.spec.draft.ts} | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) rename test/integration/{velodromeExchangeTradeModule.spec.ts => velodromeExchangeTradeModule.spec.draft.ts} (96%) diff --git a/test/integration/velodromeExchangeTradeModule.spec.ts b/test/integration/velodromeExchangeTradeModule.spec.draft.ts similarity index 96% rename from test/integration/velodromeExchangeTradeModule.spec.ts rename to test/integration/velodromeExchangeTradeModule.spec.draft.ts index 83e1b056f..76459ce7f 100644 --- a/test/integration/velodromeExchangeTradeModule.spec.ts +++ b/test/integration/velodromeExchangeTradeModule.spec.draft.ts @@ -6,12 +6,11 @@ import { ethers, network } from "hardhat"; import { Address, Bytes } from "@utils/types"; import { Account } from "@utils/test/types"; import { - IERC20, - IERC20__factory, - IVelodromeRouter, + SetToken, + TradeModule, + ManagerIssuanceHookMock, VelodromeExchangeAdapter, -} from "@typechain/index"; -import { SetToken, TradeModule, ManagerIssuanceHookMock } from "@utils/contracts"; +} from "@utils/contracts"; import { ZERO } from "@utils/constants"; import DeployHelper from "@utils/deploys"; import { ether, usdc } from "@utils/index"; @@ -19,6 +18,9 @@ import { cacheBeforeEach, getAccounts, getSystemFixture, getWaffleExpect } from import { SystemFixture } from "@utils/fixtures"; import { parseUnits } from "ethers/lib/utils"; +import { IVelodromeRouter } from "@typechain/IVelodromeRouter"; +import { IERC20 } from "@typechain/IERC20"; +import { IERC20__factory } from "@typechain/factories/IERC20__factory"; const expect = getWaffleExpect(); From cd5d9e22ef3bca75704d5572fa25fff45e0b6726 Mon Sep 17 00:00:00 2001 From: deephil0226 Date: Wed, 29 Jun 2022 20:59:49 -0400 Subject: [PATCH 08/10] fix comments --- .../exchange/VelodromeExchangeAdapter.sol | 4 +- .../contracts/velodrome/VelodromeFactory.sol | 104 ---- .../contracts/velodrome/VelodromePair.sol | 559 ------------------ .../contracts/velodrome/VelodromeRouter.sol | 427 ------------- .../contracts/velodrome/interfaces/IERC20.sol | 16 - .../interfaces/IVelodromeFactory.sol | 10 - .../velodrome/interfaces/IVelodromePair.sol | 15 - .../interfaces/IVelodromePairCallee.sol | 6 - .../velodrome/interfaces/IVelodromeRouter.sol | 6 - .../contracts/velodrome/interfaces/IWETH.sol | 8 - external/contracts/velodrome/lib/Math.sol | 35 -- ...velodromeExchangeTradeModule.spec.draft.ts | 3 +- .../exchange/velodromeExchangeAdapter.spec.ts | 80 ++- utils/deploys/deployAdapters.ts | 6 +- utils/deploys/deployExternal.ts | 13 +- 15 files changed, 94 insertions(+), 1198 deletions(-) delete mode 100644 external/contracts/velodrome/VelodromeFactory.sol delete mode 100644 external/contracts/velodrome/VelodromePair.sol delete mode 100644 external/contracts/velodrome/VelodromeRouter.sol delete mode 100644 external/contracts/velodrome/interfaces/IERC20.sol delete mode 100644 external/contracts/velodrome/interfaces/IVelodromeFactory.sol delete mode 100644 external/contracts/velodrome/interfaces/IVelodromePair.sol delete mode 100644 external/contracts/velodrome/interfaces/IVelodromePairCallee.sol delete mode 100644 external/contracts/velodrome/interfaces/IVelodromeRouter.sol delete mode 100644 external/contracts/velodrome/interfaces/IWETH.sol delete mode 100644 external/contracts/velodrome/lib/Math.sol diff --git a/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol b/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol index 7897e01b5..b9a23212c 100644 --- a/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol +++ b/contracts/protocol/integration/exchange/VelodromeExchangeAdapter.sol @@ -81,6 +81,7 @@ contract VelodromeExchangeAdapter { require(routes.length > 0, "empty routes"); require(_sourceToken == routes[0].from, "Source token path mismatch"); require(_destinationToken == routes[routes.length - 1].to, "Destination token path mismatch"); + require(deadline >= block.timestamp, "invalid deadline"); bytes memory callData = abi.encodeWithSelector( IVelodromeRouter.swapExactTokensForTokens.selector, @@ -115,10 +116,11 @@ contract VelodromeExchangeAdapter { */ function generateDataParam(IVelodromeRouter.route[] calldata _routes, uint256 _deadline) external - pure + view returns (bytes memory) { require(_routes.length > 0, "empty routes"); + require(_deadline >= block.timestamp, "invalid deadline"); return abi.encode(_routes, _deadline); } } \ No newline at end of file diff --git a/external/contracts/velodrome/VelodromeFactory.sol b/external/contracts/velodrome/VelodromeFactory.sol deleted file mode 100644 index 774aeb451..000000000 --- a/external/contracts/velodrome/VelodromeFactory.sol +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.11; - -import "./VelodromePair.sol"; -import "./interfaces/IVelodromePair.sol"; -import "./interfaces/IVelodromePairCallee.sol"; -import "./interfaces/IVelodromeFactory.sol"; - -contract VelodromeFactory is IVelodromeFactory { - bool public isPaused; - address public pauser; - address public pendingPauser; - - uint256 public stableFee; - uint256 public volatileFee; - uint256 public constant MAX_FEE = 5; // 0.05% - address public feeManager; - address public pendingFeeManager; - - mapping(address => mapping(address => mapping(bool => address))) public getPair; - address[] public allPairs; - mapping(address => bool) public isPair; // simplified check if its a pair, given that `stable` flag might not be available in peripherals - - address internal _temp0; - address internal _temp1; - bool internal _temp; - - event PairCreated(address indexed token0, address indexed token1, bool stable, address pair, uint); - - constructor() { - pauser = msg.sender; - isPaused = false; - feeManager = msg.sender; - stableFee = 2; // 0.02% - volatileFee = 2; - } - - function allPairsLength() external view returns (uint) { - return allPairs.length; - } - - function setPauser(address _pauser) external { - require(msg.sender == pauser); - pendingPauser = _pauser; - } - - function acceptPauser() external { - require(msg.sender == pendingPauser); - pauser = pendingPauser; - } - - function setPause(bool _state) external { - require(msg.sender == pauser); - isPaused = _state; - } - - function setFeeManager(address _feeManager) external { - require(msg.sender == feeManager, "not fee manager"); - pendingFeeManager = _feeManager; - } - - function acceptFeeManager() external { - require(msg.sender == pendingFeeManager, "not pending fee manager"); - feeManager = pendingFeeManager; - } - - function setFee(bool _stable, uint256 _fee) external { - require(msg.sender == feeManager, "not fee manager"); - require(_fee <= MAX_FEE, "fee too high"); - require(_fee != 0, "fee must be nonzero"); - if (_stable) { - stableFee = _fee; - } else { - volatileFee = _fee; - } - } - - function getFee(bool _stable) public view returns(uint256) { - return _stable ? stableFee : volatileFee; - } - - function pairCodeHash() external pure returns (bytes32) { - return keccak256(type(VelodromePair).creationCode); - } - - function getInitializable() external view returns (address, address, bool) { - return (_temp0, _temp1, _temp); - } - - function createPair(address tokenA, address tokenB, bool stable) external returns (address pair) { - require(tokenA != tokenB, "IA"); // Pair: IDENTICAL_ADDRESSES - (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); - require(token0 != address(0), "ZA"); // Pair: ZERO_ADDRESS - require(getPair[token0][token1][stable] == address(0), "PE"); // Pair: PAIR_EXISTS - single check is sufficient - bytes32 salt = keccak256(abi.encodePacked(token0, token1, stable)); // notice salt includes stable as well, 3 parameters - (_temp0, _temp1, _temp) = (token0, token1, stable); - pair = address(new VelodromePair{salt:salt}()); - getPair[token0][token1][stable] = pair; - getPair[token1][token0][stable] = pair; // populate mapping in the reverse direction - allPairs.push(pair); - isPair[pair] = true; - emit PairCreated(token0, token1, stable, pair, allPairs.length); - } -} diff --git a/external/contracts/velodrome/VelodromePair.sol b/external/contracts/velodrome/VelodromePair.sol deleted file mode 100644 index 3914c31bd..000000000 --- a/external/contracts/velodrome/VelodromePair.sol +++ /dev/null @@ -1,559 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.11; - -import "./VelodromeFactory.sol"; -import "./lib/Math.sol"; -import "./interfaces/IERC20.sol"; -import "./interfaces/IVelodromePair.sol"; -import "./interfaces/IVelodromePairCallee.sol"; - -// Pair Fees contract is used as a 1:1 pair relationship to split out fees, this ensures that the curve does not need to be modified for LP shares -contract VelodromePairFees { - - address internal immutable pair; // The pair it is bonded to - address internal immutable token0; // token0 of pair, saved localy and statically for gas optimization - address internal immutable token1; // Token1 of pair, saved localy and statically for gas optimization - - constructor(address _token0, address _token1) { - pair = msg.sender; - token0 = _token0; - token1 = _token1; - } - - function _safeTransfer(address token,address to,uint256 value) internal { - require(token.code.length > 0); - (bool success, bytes memory data) = - token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value)); - require(success && (data.length == 0 || abi.decode(data, (bool)))); - } - - // Allow the pair to transfer fees to users - function claimFeesFor(address recipient, uint amount0, uint amount1) external { - require(msg.sender == pair); - if (amount0 > 0) _safeTransfer(token0, recipient, amount0); - if (amount1 > 0) _safeTransfer(token1, recipient, amount1); - } - -} - -// The base pair of pools, either stable or volatile -contract VelodromePair is IVelodromePair { - - string public name; - string public symbol; - uint8 public constant decimals = 18; - - // Used to denote stable or volatile pair, not immutable since construction happens in the initialize method for CREATE2 deterministic addresses - bool public immutable stable; - - uint public totalSupply = 0; - - mapping(address => mapping (address => uint)) public allowance; - mapping(address => uint) public balanceOf; - - bytes32 internal DOMAIN_SEPARATOR; - // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); - bytes32 internal constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; - mapping(address => uint) public nonces; - - uint internal constant MINIMUM_LIQUIDITY = 10**3; - - address public immutable token0; - address public immutable token1; - address public immutable fees; - address immutable factory; - - // Structure to capture time period obervations every 30 minutes, used for local oracles - struct Observation { - uint timestamp; - uint reserve0Cumulative; - uint reserve1Cumulative; - } - - // Capture oracle reading every 30 minutes - uint constant periodSize = 1800; - - Observation[] public observations; - - uint internal immutable decimals0; - uint internal immutable decimals1; - - uint public reserve0; - uint public reserve1; - uint public blockTimestampLast; - - uint public reserve0CumulativeLast; - uint public reserve1CumulativeLast; - - // index0 and index1 are used to accumulate fees, this is split out from normal trades to keep the swap "clean" - // this further allows LP holders to easily claim fees for tokens they have/staked - uint public index0 = 0; - uint public index1 = 0; - - // position assigned to each LP to track their current index0 & index1 vs the global position - mapping(address => uint) public supplyIndex0; - mapping(address => uint) public supplyIndex1; - - // tracks the amount of unclaimed, but claimable tokens off of fees for token0 and token1 - mapping(address => uint) public claimable0; - mapping(address => uint) public claimable1; - - event Fees(address indexed sender, uint amount0, uint amount1); - event Mint(address indexed sender, uint amount0, uint amount1); - event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); - event Swap( - address indexed sender, - uint amount0In, - uint amount1In, - uint amount0Out, - uint amount1Out, - address indexed to - ); - event Sync(uint reserve0, uint reserve1); - event Claim(address indexed sender, address indexed recipient, uint amount0, uint amount1); - - event Transfer(address indexed from, address indexed to, uint amount); - event Approval(address indexed owner, address indexed spender, uint amount); - - constructor() { - factory = msg.sender; - (address _token0, address _token1, bool _stable) = VelodromeFactory(msg.sender).getInitializable(); - (token0, token1, stable) = (_token0, _token1, _stable); - fees = address(new VelodromePairFees(_token0, _token1)); - if (_stable) { - name = string(abi.encodePacked("StableV1 AMM - ", IERC20(_token0).symbol(), "/", IERC20(_token1).symbol())); - symbol = string(abi.encodePacked("sAMM-", IERC20(_token0).symbol(), "/", IERC20(_token1).symbol())); - } else { - name = string(abi.encodePacked("VolatileV1 AMM - ", IERC20(_token0).symbol(), "/", IERC20(_token1).symbol())); - symbol = string(abi.encodePacked("vAMM-", IERC20(_token0).symbol(), "/", IERC20(_token1).symbol())); - } - - decimals0 = 10**IERC20(_token0).decimals(); - decimals1 = 10**IERC20(_token1).decimals(); - - observations.push(Observation(block.timestamp, 0, 0)); - } - - // simple re-entrancy check - uint internal _unlocked = 1; - modifier lock() { - require(_unlocked == 1); - _unlocked = 2; - _; - _unlocked = 1; - } - - function observationLength() external view returns (uint) { - return observations.length; - } - - function lastObservation() public view returns (Observation memory) { - return observations[observations.length-1]; - } - - function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1) { - return (decimals0, decimals1, reserve0, reserve1, stable, token0, token1); - } - - function tokens() external view returns (address, address) { - return (token0, token1); - } - - // claim accumulated but unclaimed fees (viewable via claimable0 and claimable1) - function claimFees() external returns (uint claimed0, uint claimed1) { - _updateFor(msg.sender); - - claimed0 = claimable0[msg.sender]; - claimed1 = claimable1[msg.sender]; - - if (claimed0 > 0 || claimed1 > 0) { - claimable0[msg.sender] = 0; - claimable1[msg.sender] = 0; - - VelodromePairFees(fees).claimFeesFor(msg.sender, claimed0, claimed1); - - emit Claim(msg.sender, msg.sender, claimed0, claimed1); - } - } - - // Accrue fees on token0 - function _update0(uint amount) internal { - _safeTransfer(token0, fees, amount); // transfer the fees out to PairFees - uint256 _ratio = amount * 1e18 / totalSupply; // 1e18 adjustment is removed during claim - if (_ratio > 0) { - index0 += _ratio; - } - emit Fees(msg.sender, amount, 0); - } - - // Accrue fees on token1 - function _update1(uint amount) internal { - _safeTransfer(token1, fees, amount); - uint256 _ratio = amount * 1e18 / totalSupply; - if (_ratio > 0) { - index1 += _ratio; - } - emit Fees(msg.sender, 0, amount); - } - - // this function MUST be called on any balance changes, otherwise can be used to infinitely claim fees - // Fees are segregated from core funds, so fees can never put liquidity at risk - function _updateFor(address recipient) internal { - uint _supplied = balanceOf[recipient]; // get LP balance of `recipient` - if (_supplied > 0) { - uint _supplyIndex0 = supplyIndex0[recipient]; // get last adjusted index0 for recipient - uint _supplyIndex1 = supplyIndex1[recipient]; - uint _index0 = index0; // get global index0 for accumulated fees - uint _index1 = index1; - supplyIndex0[recipient] = _index0; // update user current position to global position - supplyIndex1[recipient] = _index1; - uint _delta0 = _index0 - _supplyIndex0; // see if there is any difference that need to be accrued - uint _delta1 = _index1 - _supplyIndex1; - if (_delta0 > 0) { - uint _share = _supplied * _delta0 / 1e18; // add accrued difference for each supplied token - claimable0[recipient] += _share; - } - if (_delta1 > 0) { - uint _share = _supplied * _delta1 / 1e18; - claimable1[recipient] += _share; - } - } else { - supplyIndex0[recipient] = index0; // new users are set to the default global state - supplyIndex1[recipient] = index1; - } - } - - function getReserves() public view returns (uint _reserve0, uint _reserve1, uint _blockTimestampLast) { - _reserve0 = reserve0; - _reserve1 = reserve1; - _blockTimestampLast = blockTimestampLast; - } - - // update reserves and, on the first call per block, price accumulators - function _update(uint balance0, uint balance1, uint _reserve0, uint _reserve1) internal { - uint blockTimestamp = block.timestamp; - uint timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired - if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) { - reserve0CumulativeLast += _reserve0 * timeElapsed; - reserve1CumulativeLast += _reserve1 * timeElapsed; - } - - Observation memory _point = lastObservation(); - timeElapsed = blockTimestamp - _point.timestamp; // compare the last observation with current timestamp, if greater than 30 minutes, record a new event - if (timeElapsed > periodSize) { - observations.push(Observation(blockTimestamp, reserve0CumulativeLast, reserve1CumulativeLast)); - } - reserve0 = balance0; - reserve1 = balance1; - blockTimestampLast = blockTimestamp; - emit Sync(reserve0, reserve1); - } - - // produces the cumulative price using counterfactuals to save gas and avoid a call to sync. - function currentCumulativePrices() public view returns (uint reserve0Cumulative, uint reserve1Cumulative, uint blockTimestamp) { - blockTimestamp = block.timestamp; - reserve0Cumulative = reserve0CumulativeLast; - reserve1Cumulative = reserve1CumulativeLast; - - // if time has elapsed since the last update on the pair, mock the accumulated price values - (uint _reserve0, uint _reserve1, uint _blockTimestampLast) = getReserves(); - if (_blockTimestampLast != blockTimestamp) { - // subtraction overflow is desired - uint timeElapsed = blockTimestamp - _blockTimestampLast; - reserve0Cumulative += _reserve0 * timeElapsed; - reserve1Cumulative += _reserve1 * timeElapsed; - } - } - - // gives the current twap price measured from amountIn * tokenIn gives amountOut - function current(address tokenIn, uint amountIn) external view returns (uint amountOut) { - Observation memory _observation = lastObservation(); - (uint reserve0Cumulative, uint reserve1Cumulative,) = currentCumulativePrices(); - if (block.timestamp == _observation.timestamp) { - _observation = observations[observations.length-2]; - } - - uint timeElapsed = block.timestamp - _observation.timestamp; - uint _reserve0 = (reserve0Cumulative - _observation.reserve0Cumulative) / timeElapsed; - uint _reserve1 = (reserve1Cumulative - _observation.reserve1Cumulative) / timeElapsed; - amountOut = _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1); - } - - // as per `current`, however allows user configured granularity, up to the full window size - function quote(address tokenIn, uint amountIn, uint granularity) external view returns (uint amountOut) { - uint [] memory _prices = sample(tokenIn, amountIn, granularity, 1); - uint priceAverageCumulative; - for (uint i = 0; i < _prices.length; i++) { - priceAverageCumulative += _prices[i]; - } - return priceAverageCumulative / granularity; - } - - // returns a memory set of twap prices - function prices(address tokenIn, uint amountIn, uint points) external view returns (uint[] memory) { - return sample(tokenIn, amountIn, points, 1); - } - - function sample(address tokenIn, uint amountIn, uint points, uint window) public view returns (uint[] memory) { - uint[] memory _prices = new uint[](points); - - uint length = observations.length-1; - uint i = length - (points * window); - uint nextIndex = 0; - uint index = 0; - - for (; i < length; i+=window) { - nextIndex = i + window; - uint timeElapsed = observations[nextIndex].timestamp - observations[i].timestamp; - uint _reserve0 = (observations[nextIndex].reserve0Cumulative - observations[i].reserve0Cumulative) / timeElapsed; - uint _reserve1 = (observations[nextIndex].reserve1Cumulative - observations[i].reserve1Cumulative) / timeElapsed; - _prices[index] = _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1); - // index < length; length cannot overflow - unchecked { - index = index + 1; - } - } - return _prices; - } - - // this low-level function should be called by addLiquidity functions in Router.sol, which performs important safety checks - // standard uniswap v2 implementation - function mint(address to) external lock returns (uint liquidity) { - (uint _reserve0, uint _reserve1) = (reserve0, reserve1); - uint _balance0 = IERC20(token0).balanceOf(address(this)); - uint _balance1 = IERC20(token1).balanceOf(address(this)); - uint _amount0 = _balance0 - _reserve0; - uint _amount1 = _balance1 - _reserve1; - - uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee - if (_totalSupply == 0) { - liquidity = Math.sqrt(_amount0 * _amount1) - MINIMUM_LIQUIDITY; - _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens - } else { - liquidity = Math.min(_amount0 * _totalSupply / _reserve0, _amount1 * _totalSupply / _reserve1); - } - require(liquidity > 0, "ILM"); // Pair: INSUFFICIENT_LIQUIDITY_MINTED - _mint(to, liquidity); - - _update(_balance0, _balance1, _reserve0, _reserve1); - emit Mint(msg.sender, _amount0, _amount1); - } - - // this low-level function should be called from a contract which performs important safety checks - // standard uniswap v2 implementation - function burn(address to) external lock returns (uint amount0, uint amount1) { - (uint _reserve0, uint _reserve1) = (reserve0, reserve1); - (address _token0, address _token1) = (token0, token1); - uint _balance0 = IERC20(_token0).balanceOf(address(this)); - uint _balance1 = IERC20(_token1).balanceOf(address(this)); - uint _liquidity = balanceOf[address(this)]; - - uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee - amount0 = _liquidity * _balance0 / _totalSupply; // using balances ensures pro-rata distribution - amount1 = _liquidity * _balance1 / _totalSupply; // using balances ensures pro-rata distribution - require(amount0 > 0 && amount1 > 0, "ILB"); // Pair: INSUFFICIENT_LIQUIDITY_BURNED - _burn(address(this), _liquidity); - _safeTransfer(_token0, to, amount0); - _safeTransfer(_token1, to, amount1); - _balance0 = IERC20(_token0).balanceOf(address(this)); - _balance1 = IERC20(_token1).balanceOf(address(this)); - - _update(_balance0, _balance1, _reserve0, _reserve1); - emit Burn(msg.sender, amount0, amount1, to); - } - - // this low-level function should be called from a contract which performs important safety checks - function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock { - require(!VelodromeFactory(factory).isPaused()); - require(amount0Out > 0 || amount1Out > 0, "IOA"); // Pair: INSUFFICIENT_OUTPUT_AMOUNT - (uint _reserve0, uint _reserve1) = (reserve0, reserve1); - require(amount0Out < _reserve0 && amount1Out < _reserve1, "IL"); // Pair: INSUFFICIENT_LIQUIDITY - - uint _balance0; - uint _balance1; - { // scope for _token{0,1}, avoids stack too deep errors - (address _token0, address _token1) = (token0, token1); - require(to != _token0 && to != _token1, "IT"); // Pair: INVALID_TO - if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens - if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens - if (data.length > 0) IVelodromePairCallee(to).hook(msg.sender, amount0Out, amount1Out, data); // callback, used for flash loans - _balance0 = IERC20(_token0).balanceOf(address(this)); - _balance1 = IERC20(_token1).balanceOf(address(this)); - } - uint amount0In = _balance0 > _reserve0 - amount0Out ? _balance0 - (_reserve0 - amount0Out) : 0; - uint amount1In = _balance1 > _reserve1 - amount1Out ? _balance1 - (_reserve1 - amount1Out) : 0; - require(amount0In > 0 || amount1In > 0, "IIA"); // Pair: INSUFFICIENT_INPUT_AMOUNT - { // scope for reserve{0,1}Adjusted, avoids stack too deep errors - (address _token0, address _token1) = (token0, token1); - if (amount0In > 0) _update0(amount0In * VelodromeFactory(factory).getFee(stable) / 10000); // accrue fees for token0 and move them out of pool - if (amount1In > 0) _update1(amount1In * VelodromeFactory(factory).getFee(stable) / 10000); // accrue fees for token1 and move them out of pool - _balance0 = IERC20(_token0).balanceOf(address(this)); // since we removed tokens, we need to reconfirm balances, can also simply use previous balance - amountIn/ 10000, but doing balanceOf again as safety check - _balance1 = IERC20(_token1).balanceOf(address(this)); - // The curve, either x3y+y3x for stable pools, or x*y for volatile pools - require(_k(_balance0, _balance1) >= _k(_reserve0, _reserve1), "K"); // Pair: K - } - - _update(_balance0, _balance1, _reserve0, _reserve1); - emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to); - } - - // force balances to match reserves - function skim(address to) external lock { - (address _token0, address _token1) = (token0, token1); - _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)) - (reserve0)); - _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)) - (reserve1)); - } - - // force reserves to match balances - function sync() external lock { - _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1); - } - - function _f(uint x0, uint y) internal pure returns (uint) { - return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18; - } - - function _d(uint x0, uint y) internal pure returns (uint) { - return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18); - } - - function _get_y(uint x0, uint xy, uint y) internal pure returns (uint) { - for (uint i = 0; i < 255; i++) { - uint y_prev = y; - uint k = _f(x0, y); - if (k < xy) { - uint dy = (xy - k)*1e18/_d(x0, y); - y = y + dy; - } else { - uint dy = (k - xy)*1e18/_d(x0, y); - y = y - dy; - } - if (y > y_prev) { - if (y - y_prev <= 1) { - return y; - } - } else { - if (y_prev - y <= 1) { - return y; - } - } - } - return y; - } - - function getAmountOut(uint amountIn, address tokenIn) external view returns (uint) { - (uint _reserve0, uint _reserve1) = (reserve0, reserve1); - amountIn -= amountIn * VelodromeFactory(factory).getFee(stable) / 10000; // remove fee from amount received - return _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1); - } - - function _getAmountOut(uint amountIn, address tokenIn, uint _reserve0, uint _reserve1) internal view returns (uint) { - if (stable) { - uint xy = _k(_reserve0, _reserve1); - _reserve0 = _reserve0 * 1e18 / decimals0; - _reserve1 = _reserve1 * 1e18 / decimals1; - (uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0); - amountIn = tokenIn == token0 ? amountIn * 1e18 / decimals0 : amountIn * 1e18 / decimals1; - uint y = reserveB - _get_y(amountIn+reserveA, xy, reserveB); - return y * (tokenIn == token0 ? decimals1 : decimals0) / 1e18; - } else { - (uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0); - return amountIn * reserveB / (reserveA + amountIn); - } - } - - function _k(uint x, uint y) internal view returns (uint) { - if (stable) { - uint _x = x * 1e18 / decimals0; - uint _y = y * 1e18 / decimals1; - uint _a = (_x * _y) / 1e18; - uint _b = ((_x * _x) / 1e18 + (_y * _y) / 1e18); - return _a * _b / 1e18; // x3y+y3x >= k - } else { - return x * y; // xy >= k - } - } - - function _mint(address dst, uint amount) internal { - _updateFor(dst); // balances must be updated on mint/burn/transfer - totalSupply += amount; - balanceOf[dst] += amount; - emit Transfer(address(0), dst, amount); - } - - function _burn(address dst, uint amount) internal { - _updateFor(dst); - totalSupply -= amount; - balanceOf[dst] -= amount; - emit Transfer(dst, address(0), amount); - } - - function approve(address spender, uint amount) external returns (bool) { - allowance[msg.sender][spender] = amount; - - emit Approval(msg.sender, spender, amount); - return true; - } - - function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external { - require(deadline >= block.timestamp, "Pair: EXPIRED"); - DOMAIN_SEPARATOR = keccak256( - abi.encode( - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), - keccak256(bytes(name)), - keccak256(bytes("1")), - block.chainid, - address(this) - ) - ); - bytes32 digest = keccak256( - abi.encodePacked( - "\x19\x01", - DOMAIN_SEPARATOR, - keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)) - ) - ); - address recoveredAddress = ecrecover(digest, v, r, s); - require(recoveredAddress != address(0) && recoveredAddress == owner, "Pair: INVALID_SIGNATURE"); - allowance[owner][spender] = value; - - emit Approval(owner, spender, value); - } - - function transfer(address dst, uint amount) external returns (bool) { - _transferTokens(msg.sender, dst, amount); - return true; - } - - function transferFrom(address src, address dst, uint amount) external returns (bool) { - address spender = msg.sender; - uint spenderAllowance = allowance[src][spender]; - - if (spender != src && spenderAllowance != type(uint).max) { - uint newAllowance = spenderAllowance - amount; - allowance[src][spender] = newAllowance; - - emit Approval(src, spender, newAllowance); - } - - _transferTokens(src, dst, amount); - return true; - } - - function _transferTokens(address src, address dst, uint amount) internal { - _updateFor(src); // update fee position for src - _updateFor(dst); // update fee position for dst - - balanceOf[src] -= amount; - balanceOf[dst] += amount; - - emit Transfer(src, dst, amount); - } - - function _safeTransfer(address token,address to,uint256 value) internal { - require(token.code.length > 0); - (bool success, bytes memory data) = - token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value)); - require(success && (data.length == 0 || abi.decode(data, (bool)))); - } -} diff --git a/external/contracts/velodrome/VelodromeRouter.sol b/external/contracts/velodrome/VelodromeRouter.sol deleted file mode 100644 index 5d9674005..000000000 --- a/external/contracts/velodrome/VelodromeRouter.sol +++ /dev/null @@ -1,427 +0,0 @@ -/** - *Submitted for verification at optimistic.etherscan.io on 2022-06-01 -*/ - -// SPDX-License-Identifier: MIT - -pragma solidity 0.8.11; - -import "./lib/Math.sol"; -import "./interfaces/IVelodromeRouter.sol"; -import "./interfaces/IWETH.sol"; -import "./interfaces/IERC20.sol"; -import "./interfaces/IVelodromeFactory.sol"; -import "./interfaces/IVelodromePair.sol"; - -contract VelodromeRouter is IVelodromeRouter { - - struct route { - address from; - address to; - bool stable; - } - - address public immutable factory; - IWETH public immutable weth; - uint internal constant MINIMUM_LIQUIDITY = 10**3; - bytes32 immutable pairCodeHash; - - modifier ensure(uint deadline) { - require(deadline >= block.timestamp, "Router: EXPIRED"); - _; - } - - constructor(address _factory, address _weth) { - factory = _factory; - pairCodeHash = IVelodromeFactory(_factory).pairCodeHash(); - weth = IWETH(_weth); - } - - receive() external payable { - assert(msg.sender == address(weth)); // only accept ETH via fallback from the WETH contract - } - - function sortTokens(address tokenA, address tokenB) public pure returns (address token0, address token1) { - require(tokenA != tokenB, "Router: IDENTICAL_ADDRESSES"); - (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); - require(token0 != address(0), "Router: ZERO_ADDRESS"); - } - - // calculates the CREATE2 address for a pair without making any external calls - function pairFor(address tokenA, address tokenB, bool stable) public view returns (address pair) { - (address token0, address token1) = sortTokens(tokenA, tokenB); - pair = address(uint160(uint256(keccak256(abi.encodePacked( - hex"ff", - factory, - keccak256(abi.encodePacked(token0, token1, stable)), - pairCodeHash // init code hash - ))))); - } - - // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset - function quoteLiquidity(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) { - require(amountA > 0, "Router: INSUFFICIENT_AMOUNT"); - require(reserveA > 0 && reserveB > 0, "Router: INSUFFICIENT_LIQUIDITY"); - amountB = amountA * reserveB / reserveA; - } - - // fetches and sorts the reserves for a pair - function getReserves(address tokenA, address tokenB, bool stable) public view returns (uint reserveA, uint reserveB) { - (address token0,) = sortTokens(tokenA, tokenB); - (uint reserve0, uint reserve1,) = IVelodromePair(pairFor(tokenA, tokenB, stable)).getReserves(); - (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0); - } - - // performs chained getAmountOut calculations on any number of pairs - function getAmountOut(uint amountIn, address tokenIn, address tokenOut) external view returns (uint amount, bool stable) { - address pair = pairFor(tokenIn, tokenOut, true); - uint amountStable; - uint amountVolatile; - if (IVelodromeFactory(factory).isPair(pair)) { - amountStable = IVelodromePair(pair).getAmountOut(amountIn, tokenIn); - } - pair = pairFor(tokenIn, tokenOut, false); - if (IVelodromeFactory(factory).isPair(pair)) { - amountVolatile = IVelodromePair(pair).getAmountOut(amountIn, tokenIn); - } - return amountStable > amountVolatile ? (amountStable, true) : (amountVolatile, false); - } - - // performs chained getAmountOut calculations on any number of pairs - function getAmountsOut(uint amountIn, route[] memory routes) public view returns (uint[] memory amounts) { - require(routes.length >= 1, "Router: INVALID_PATH"); - amounts = new uint[](routes.length+1); - amounts[0] = amountIn; - for (uint i = 0; i < routes.length; i++) { - address pair = pairFor(routes[i].from, routes[i].to, routes[i].stable); - if (IVelodromeFactory(factory).isPair(pair)) { - amounts[i+1] = IVelodromePair(pair).getAmountOut(amounts[i], routes[i].from); - } - } - } - - function isPair(address pair) external view returns (bool) { - return IVelodromeFactory(factory).isPair(pair); - } - - function quoteAddLiquidity( - address tokenA, - address tokenB, - bool stable, - uint amountADesired, - uint amountBDesired - ) external view returns (uint amountA, uint amountB, uint liquidity) { - // create the pair if it doesn"t exist yet - address _pair = IVelodromeFactory(factory).getPair(tokenA, tokenB, stable); - (uint reserveA, uint reserveB) = (0,0); - uint _totalSupply = 0; - if (_pair != address(0)) { - _totalSupply = IERC20(_pair).totalSupply(); - (reserveA, reserveB) = getReserves(tokenA, tokenB, stable); - } - if (reserveA == 0 && reserveB == 0) { - (amountA, amountB) = (amountADesired, amountBDesired); - liquidity = Math.sqrt(amountA * amountB) - MINIMUM_LIQUIDITY; - } else { - - uint amountBOptimal = quoteLiquidity(amountADesired, reserveA, reserveB); - if (amountBOptimal <= amountBDesired) { - (amountA, amountB) = (amountADesired, amountBOptimal); - liquidity = Math.min(amountA * _totalSupply / reserveA, amountB * _totalSupply / reserveB); - } else { - uint amountAOptimal = quoteLiquidity(amountBDesired, reserveB, reserveA); - (amountA, amountB) = (amountAOptimal, amountBDesired); - liquidity = Math.min(amountA * _totalSupply / reserveA, amountB * _totalSupply / reserveB); - } - } - } - - function quoteRemoveLiquidity( - address tokenA, - address tokenB, - bool stable, - uint liquidity - ) external view returns (uint amountA, uint amountB) { - // create the pair if it doesn"t exist yet - address _pair = IVelodromeFactory(factory).getPair(tokenA, tokenB, stable); - - if (_pair == address(0)) { - return (0,0); - } - - (uint reserveA, uint reserveB) = getReserves(tokenA, tokenB, stable); - uint _totalSupply = IERC20(_pair).totalSupply(); - - amountA = liquidity * reserveA / _totalSupply; // using balances ensures pro-rata distribution - amountB = liquidity * reserveB / _totalSupply; // using balances ensures pro-rata distribution - - } - - function _addLiquidity( - address tokenA, - address tokenB, - bool stable, - uint amountADesired, - uint amountBDesired, - uint amountAMin, - uint amountBMin - ) internal returns (uint amountA, uint amountB) { - require(amountADesired >= amountAMin); - require(amountBDesired >= amountBMin); - // create the pair if it doesn"t exist yet - address _pair = IVelodromeFactory(factory).getPair(tokenA, tokenB, stable); - if (_pair == address(0)) { - _pair = IVelodromeFactory(factory).createPair(tokenA, tokenB, stable); - } - (uint reserveA, uint reserveB) = getReserves(tokenA, tokenB, stable); - if (reserveA == 0 && reserveB == 0) { - (amountA, amountB) = (amountADesired, amountBDesired); - } else { - uint amountBOptimal = quoteLiquidity(amountADesired, reserveA, reserveB); - if (amountBOptimal <= amountBDesired) { - require(amountBOptimal >= amountBMin, "Router: INSUFFICIENT_B_AMOUNT"); - (amountA, amountB) = (amountADesired, amountBOptimal); - } else { - uint amountAOptimal = quoteLiquidity(amountBDesired, reserveB, reserveA); - assert(amountAOptimal <= amountADesired); - require(amountAOptimal >= amountAMin, "Router: INSUFFICIENT_A_AMOUNT"); - (amountA, amountB) = (amountAOptimal, amountBDesired); - } - } - } - - function addLiquidity( - address tokenA, - address tokenB, - bool stable, - uint amountADesired, - uint amountBDesired, - uint amountAMin, - uint amountBMin, - address to, - uint deadline - ) external ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) { - (amountA, amountB) = _addLiquidity(tokenA, tokenB, stable, amountADesired, amountBDesired, amountAMin, amountBMin); - address pair = pairFor(tokenA, tokenB, stable); - _safeTransferFrom(tokenA, msg.sender, pair, amountA); - _safeTransferFrom(tokenB, msg.sender, pair, amountB); - liquidity = IVelodromePair(pair).mint(to); - } - - function addLiquidityETH( - address token, - bool stable, - uint amountTokenDesired, - uint amountTokenMin, - uint amountETHMin, - address to, - uint deadline - ) external payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) { - (amountToken, amountETH) = _addLiquidity( - token, - address(weth), - stable, - amountTokenDesired, - msg.value, - amountTokenMin, - amountETHMin - ); - address pair = pairFor(token, address(weth), stable); - _safeTransferFrom(token, msg.sender, pair, amountToken); - weth.deposit{value: amountETH}(); - assert(weth.transfer(pair, amountETH)); - liquidity = IVelodromePair(pair).mint(to); - // refund dust eth, if any - if (msg.value > amountETH) _safeTransferETH(msg.sender, msg.value - amountETH); - } - - // **** REMOVE LIQUIDITY **** - function removeLiquidity( - address tokenA, - address tokenB, - bool stable, - uint liquidity, - uint amountAMin, - uint amountBMin, - address to, - uint deadline - ) public ensure(deadline) returns (uint amountA, uint amountB) { - address pair = pairFor(tokenA, tokenB, stable); - require(IVelodromePair(pair).transferFrom(msg.sender, pair, liquidity)); // send liquidity to pair - (uint amount0, uint amount1) = IVelodromePair(pair).burn(to); - (address token0,) = sortTokens(tokenA, tokenB); - (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0); - require(amountA >= amountAMin, "Router: INSUFFICIENT_A_AMOUNT"); - require(amountB >= amountBMin, "Router: INSUFFICIENT_B_AMOUNT"); - } - - function removeLiquidityETH( - address token, - bool stable, - uint liquidity, - uint amountTokenMin, - uint amountETHMin, - address to, - uint deadline - ) public ensure(deadline) returns (uint amountToken, uint amountETH) { - (amountToken, amountETH) = removeLiquidity( - token, - address(weth), - stable, - liquidity, - amountTokenMin, - amountETHMin, - address(this), - deadline - ); - _safeTransfer(token, to, amountToken); - weth.withdraw(amountETH); - _safeTransferETH(to, amountETH); - } - - function removeLiquidityWithPermit( - address tokenA, - address tokenB, - bool stable, - uint liquidity, - uint amountAMin, - uint amountBMin, - address to, - uint deadline, - bool approveMax, uint8 v, bytes32 r, bytes32 s - ) external returns (uint amountA, uint amountB) { - address pair = pairFor(tokenA, tokenB, stable); - { - uint value = approveMax ? type(uint).max : liquidity; - IVelodromePair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); - } - - (amountA, amountB) = removeLiquidity(tokenA, tokenB, stable, liquidity, amountAMin, amountBMin, to, deadline); - } - - function removeLiquidityETHWithPermit( - address token, - bool stable, - uint liquidity, - uint amountTokenMin, - uint amountETHMin, - address to, - uint deadline, - bool approveMax, uint8 v, bytes32 r, bytes32 s - ) external returns (uint amountToken, uint amountETH) { - address pair = pairFor(token, address(weth), stable); - uint value = approveMax ? type(uint).max : liquidity; - IVelodromePair(pair).permit(msg.sender, address(this), value, deadline, v, r, s); - (amountToken, amountETH) = removeLiquidityETH(token, stable, liquidity, amountTokenMin, amountETHMin, to, deadline); - } - - // **** SWAP **** - // requires the initial amount to have already been sent to the first pair - function _swap(uint[] memory amounts, route[] memory routes, address _to) internal virtual { - for (uint i = 0; i < routes.length; i++) { - (address token0,) = sortTokens(routes[i].from, routes[i].to); - uint amountOut = amounts[i + 1]; - (uint amount0Out, uint amount1Out) = routes[i].from == token0 ? (uint(0), amountOut) : (amountOut, uint(0)); - address to = i < routes.length - 1 ? pairFor(routes[i+1].from, routes[i+1].to, routes[i+1].stable) : _to; - IVelodromePair(pairFor(routes[i].from, routes[i].to, routes[i].stable)).swap( - amount0Out, amount1Out, to, new bytes(0) - ); - } - } - - function swapExactTokensForTokensSimple( - uint amountIn, - uint amountOutMin, - address tokenFrom, - address tokenTo, - bool stable, - address to, - uint deadline - ) external ensure(deadline) returns (uint[] memory amounts) { - route[] memory routes = new route[](1); - routes[0].from = tokenFrom; - routes[0].to = tokenTo; - routes[0].stable = stable; - amounts = getAmountsOut(amountIn, routes); - require(amounts[amounts.length - 1] >= amountOutMin, "Router: INSUFFICIENT_OUTPUT_AMOUNT"); - _safeTransferFrom( - routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0] - ); - _swap(amounts, routes, to); - } - - function swapExactTokensForTokens( - uint amountIn, - uint amountOutMin, - route[] calldata routes, - address to, - uint deadline - ) external ensure(deadline) returns (uint[] memory amounts) { - amounts = getAmountsOut(amountIn, routes); - require(amounts[amounts.length - 1] >= amountOutMin, "Router: INSUFFICIENT_OUTPUT_AMOUNT"); - _safeTransferFrom( - routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0] - ); - _swap(amounts, routes, to); - } - - function swapExactETHForTokens(uint amountOutMin, route[] calldata routes, address to, uint deadline) - external - payable - ensure(deadline) - returns (uint[] memory amounts) - { - require(routes[0].from == address(weth), "Router: INVALID_PATH"); - amounts = getAmountsOut(msg.value, routes); - require(amounts[amounts.length - 1] >= amountOutMin, "Router: INSUFFICIENT_OUTPUT_AMOUNT"); - weth.deposit{value: amounts[0]}(); - assert(weth.transfer(pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0])); - _swap(amounts, routes, to); - } - - function swapExactTokensForETH(uint amountIn, uint amountOutMin, route[] calldata routes, address to, uint deadline) - external - ensure(deadline) - returns (uint[] memory amounts) - { - require(routes[routes.length - 1].to == address(weth), "Router: INVALID_PATH"); - amounts = getAmountsOut(amountIn, routes); - require(amounts[amounts.length - 1] >= amountOutMin, "Router: INSUFFICIENT_OUTPUT_AMOUNT"); - _safeTransferFrom( - routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0] - ); - _swap(amounts, routes, address(this)); - weth.withdraw(amounts[amounts.length - 1]); - _safeTransferETH(to, amounts[amounts.length - 1]); - } - - function UNSAFE_swapExactTokensForTokens( - uint[] memory amounts, - route[] calldata routes, - address to, - uint deadline - ) external ensure(deadline) returns (uint[] memory) { - _safeTransferFrom(routes[0].from, msg.sender, pairFor(routes[0].from, routes[0].to, routes[0].stable), amounts[0]); - _swap(amounts, routes, to); - return amounts; - } - - function _safeTransferETH(address to, uint value) internal { - (bool success,) = to.call{value:value}(new bytes(0)); - require(success, "TransferHelper: ETH_TRANSFER_FAILED"); - } - - function _safeTransfer(address token, address to, uint256 value) internal { - require(token.code.length > 0); - (bool success, bytes memory data) = - token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value)); - require(success && (data.length == 0 || abi.decode(data, (bool)))); - } - - function _safeTransferFrom(address token, address from, address to, uint256 value) internal { - require(token.code.length > 0); - (bool success, bytes memory data) = - token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value)); - require(success && (data.length == 0 || abi.decode(data, (bool)))); - } -} diff --git a/external/contracts/velodrome/interfaces/IERC20.sol b/external/contracts/velodrome/interfaces/IERC20.sol deleted file mode 100644 index 7b20707b5..000000000 --- a/external/contracts/velodrome/interfaces/IERC20.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.11; - -interface IERC20 { - function totalSupply() external view returns (uint256); - function transfer(address recipient, uint amount) external returns (bool); - function decimals() external view returns (uint8); - function symbol() external view returns (string memory); - function balanceOf(address) external view returns (uint); - function transferFrom(address sender, address recipient, uint amount) external returns (bool); - function allowance(address owner, address spender) external view returns (uint); - function approve(address spender, uint value) external returns (bool); - - event Transfer(address indexed from, address indexed to, uint value); - event Approval(address indexed owner, address indexed spender, uint value); -} diff --git a/external/contracts/velodrome/interfaces/IVelodromeFactory.sol b/external/contracts/velodrome/interfaces/IVelodromeFactory.sol deleted file mode 100644 index 222b6d4b3..000000000 --- a/external/contracts/velodrome/interfaces/IVelodromeFactory.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.11; - -interface IVelodromeFactory { - function allPairsLength() external view returns (uint); - function isPair(address pair) external view returns (bool); - function pairCodeHash() external pure returns (bytes32); - function getPair(address tokenA, address token, bool stable) external view returns (address); - function createPair(address tokenA, address tokenB, bool stable) external returns (address pair); -} diff --git a/external/contracts/velodrome/interfaces/IVelodromePair.sol b/external/contracts/velodrome/interfaces/IVelodromePair.sol deleted file mode 100644 index 403164f73..000000000 --- a/external/contracts/velodrome/interfaces/IVelodromePair.sol +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.11; - -interface IVelodromePair { - function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1); - function claimFees() external returns (uint, uint); - function tokens() external returns (address, address); - function transferFrom(address src, address dst, uint amount) external returns (bool); - function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; - function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; - function burn(address to) external returns (uint amount0, uint amount1); - function mint(address to) external returns (uint liquidity); - function getReserves() external view returns (uint _reserve0, uint _reserve1, uint _blockTimestampLast); - function getAmountOut(uint, address) external view returns (uint); -} diff --git a/external/contracts/velodrome/interfaces/IVelodromePairCallee.sol b/external/contracts/velodrome/interfaces/IVelodromePairCallee.sol deleted file mode 100644 index 2dd40498d..000000000 --- a/external/contracts/velodrome/interfaces/IVelodromePairCallee.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.11; - -interface IVelodromePairCallee { - function hook(address sender, uint amount0, uint amount1, bytes calldata data) external; -} diff --git a/external/contracts/velodrome/interfaces/IVelodromeRouter.sol b/external/contracts/velodrome/interfaces/IVelodromeRouter.sol deleted file mode 100644 index f4cc64bd3..000000000 --- a/external/contracts/velodrome/interfaces/IVelodromeRouter.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.11; - -interface IVelodromeRouter { - function pairFor(address tokenA, address tokenB, bool stable) external view returns (address pair); -} diff --git a/external/contracts/velodrome/interfaces/IWETH.sol b/external/contracts/velodrome/interfaces/IWETH.sol deleted file mode 100644 index ea9475654..000000000 --- a/external/contracts/velodrome/interfaces/IWETH.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.11; - -interface IWETH { - function deposit() external payable returns (uint); - function transfer(address to, uint value) external returns (bool); - function withdraw(uint) external returns (uint); -} diff --git a/external/contracts/velodrome/lib/Math.sol b/external/contracts/velodrome/lib/Math.sol deleted file mode 100644 index 18b75bbdb..000000000 --- a/external/contracts/velodrome/lib/Math.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.11; - -library Math { - function max(uint a, uint b) internal pure returns (uint) { - return a >= b ? a : b; - } - function min(uint a, uint b) internal pure returns (uint) { - return a < b ? a : b; - } - 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; - } - } - function cbrt(uint256 n) internal pure returns (uint256) { unchecked { - uint256 x = 0; - for (uint256 y = 1 << 255; y > 0; y >>= 3) { - x <<= 1; - uint256 z = 3 * x * (x + 1) + 1; - if (n / y >= z) { - n -= y * z; - x += 1; - } - } - return x; - }} -} diff --git a/test/integration/velodromeExchangeTradeModule.spec.draft.ts b/test/integration/velodromeExchangeTradeModule.spec.draft.ts index 76459ce7f..b2154b47c 100644 --- a/test/integration/velodromeExchangeTradeModule.spec.draft.ts +++ b/test/integration/velodromeExchangeTradeModule.spec.draft.ts @@ -21,6 +21,7 @@ import { parseUnits } from "ethers/lib/utils"; import { IVelodromeRouter } from "@typechain/IVelodromeRouter"; import { IERC20 } from "@typechain/IERC20"; import { IERC20__factory } from "@typechain/factories/IERC20__factory"; +import dependencies from "@utils/deploys/dependencies"; const expect = getWaffleExpect(); @@ -58,7 +59,7 @@ describe("Velodrome TradeModule Integration [@optimism]", () => { velodromeRouter = await ethers.getContractAt( "IVelodromeRouter", - "0xa132DAB612dB5cB9fC9Ac426A0Cc215A3423F9c9", + dependencies.VELODROME_ROUTER[10], ); velodromeExchangeAdapter = await deployer.adapters.deployVelodromeExchangeAdapter( diff --git a/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts b/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts index 79a84cdfc..35c4c8aec 100644 --- a/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts +++ b/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts @@ -78,7 +78,7 @@ describe("VelodromeExchangeAdapter", () => { }); }); - describe("getTradeCalldata", async () => { + describe("generateDataParam / getTradeCalldata", async () => { let sourceAddress: Address; let destinationAddress: Address; let sourceQuantity: BigNumber; @@ -115,6 +115,84 @@ describe("VelodromeExchangeAdapter", () => { ); } + describe("should check parameters", () => { + it("should check params of generateDataParam", async () => { + await expect( + velodromeExchangeAdapter.generateDataParam([], ethers.constants.MaxUint256), + ).to.revertedWith("empty routes"); + await expect( + velodromeExchangeAdapter.generateDataParam( + [ + { + from: sourceAddress, + to: destinationAddress, + stable: false, + }, + ], + 0, + ), + ).to.revertedWith("invalid deadline"); + }); + + it("should check params of getTradeCalldata", async () => { + await expect( + velodromeExchangeAdapter.getTradeCalldata( + subjectSourceToken, + subjectDestinationToken, + subjectMockSetToken, + subjectSourceQuantity, + subjectMinDestinationQuantity, + ethers.utils.defaultAbiCoder.encode( + ["tuple(address,address,bool)[]", "uint256"], + [[], ethers.constants.MaxUint256], + ), + ), + ).to.revertedWith("empty routes"); + + await expect( + velodromeExchangeAdapter.getTradeCalldata( + subjectDestinationToken, + subjectDestinationToken, + subjectMockSetToken, + subjectSourceQuantity, + subjectMinDestinationQuantity, + ethers.utils.defaultAbiCoder.encode( + ["tuple(address,address,bool)[]", "uint256"], + [[[sourceAddress, destinationAddress, false]], ethers.constants.MaxUint256], + ), + ), + ).to.revertedWith("Source token path mismatch"); + + await expect( + velodromeExchangeAdapter.getTradeCalldata( + subjectSourceToken, + subjectSourceToken, + subjectMockSetToken, + subjectSourceQuantity, + subjectMinDestinationQuantity, + ethers.utils.defaultAbiCoder.encode( + ["tuple(address,address,bool)[]", "uint256"], + [[[sourceAddress, destinationAddress, false]], ethers.constants.MaxUint256], + ), + ), + ).to.revertedWith("Destination token path mismatch"); + + await expect( + velodromeExchangeAdapter.getTradeCalldata( + subjectSourceToken, + subjectDestinationToken, + subjectMockSetToken, + subjectSourceQuantity, + subjectMinDestinationQuantity, + ethers.utils.defaultAbiCoder.encode( + ["tuple(address,address,bool)[]", "uint256"], + [[[sourceAddress, destinationAddress, false]], 0], + ), + ), + ).to.revertedWith("invalid deadline"); + }); + }); + describe("wbtc -> dai", async () => { it("should return the correct data param", async () => { subjectData = await velodromeExchangeAdapter.generateDataParam( diff --git a/utils/deploys/deployAdapters.ts b/utils/deploys/deployAdapters.ts index 4716a0955..03360169d 100644 --- a/utils/deploys/deployAdapters.ts +++ b/utils/deploys/deployAdapters.ts @@ -300,8 +300,10 @@ export default class DeployAdapters { } public async deployVelodromeExchangeAdapter( - velodromRouter: Address, + velodromeRouter: Address, ): Promise { - return await new VelodromeExchangeAdapter__factory(this._deployerSigner).deploy(velodromRouter); + return await new VelodromeExchangeAdapter__factory(this._deployerSigner).deploy( + velodromeRouter, + ); } } diff --git a/utils/deploys/deployExternal.ts b/utils/deploys/deployExternal.ts index cd91cf123..822b0cc06 100644 --- a/utils/deploys/deployExternal.ts +++ b/utils/deploys/deployExternal.ts @@ -202,13 +202,12 @@ import { PerpV2ClearingHouseConfig__factory } from "../../typechain/factories/Pe import { PerpV2InsuranceFund__factory } from "../../typechain/factories/PerpV2InsuranceFund__factory"; import { PerpV2AccountBalance__factory } from "../../typechain/factories/PerpV2AccountBalance__factory"; import { PerpV2Exchange__factory } from "../../typechain/factories/PerpV2Exchange__factory"; -import { VelodromeFactory } from "@typechain/VelodromeFactory"; -import { VelodromeRouter } from "@typechain/VelodromeRouter"; -import { VelodromePair } from "@typechain/VelodromePair"; -import { VelodromeFactory__factory } from "@typechain/factories/VelodromeFactory__factory"; -import { VelodromeRouter__factory } from "@typechain/factories/VelodromeRouter__factory"; -import { VelodromePair__factory } from "@typechain/factories/VelodromePair__factory"; - +import { VelodromeFactory__factory } from "../../typechain/factories/VelodromeFactory__factory"; +import { VelodromeRouter__factory } from "../../typechain/factories/VelodromeRouter__factory"; +import { VelodromePair__factory } from "../../typechain/factories/VelodromePair__factory"; +import { VelodromeFactory } from "../../typechain/VelodromeFactory"; +import { VelodromeRouter } from "../../typechain/VelodromeRouter"; +import { VelodromePair } from "../../typechain/VelodromePair"; export default class DeployExternalContracts { private _deployerSigner: Signer; From 4d9ed79259065c8d83792c2d8d1cbf90eeefa7b4 Mon Sep 17 00:00:00 2001 From: deephil0226 Date: Thu, 30 Jun 2022 08:44:40 -0400 Subject: [PATCH 09/10] fix comments --- .../exchange/velodromeExchangeAdapter.spec.ts | 353 +++++++++--------- utils/contracts/velodrome.ts | 10 +- utils/fixtures/index.ts | 1 + utils/fixtures/velodromeFixture.ts | 10 +- utils/test/index.ts | 4 +- 5 files changed, 186 insertions(+), 192 deletions(-) diff --git a/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts b/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts index 35c4c8aec..b2fd66192 100644 --- a/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts +++ b/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts @@ -1,8 +1,7 @@ import "module-alias/register"; +import { ethers } from "hardhat"; import { BigNumber } from "ethers"; -import { utils } from "ethers"; - import { Address, Bytes } from "@utils/types"; import { Account } from "@utils/test/types"; import { ZERO } from "@utils/constants"; @@ -17,9 +16,7 @@ import { getLastBlockTimestamp, getVelodromeFixture, } from "@utils/test/index"; -import { SystemFixture } from "@utils/fixtures"; -import { VelodromeFixture } from "@utils/fixtures/velodromeFixture"; -import { ethers } from "hardhat"; +import { SystemFixture, VelodromeFixture } from "@utils/fixtures"; const expect = getWaffleExpect(); describe("VelodromeExchangeAdapter", () => { @@ -30,6 +27,11 @@ describe("VelodromeExchangeAdapter", () => { let velodromeSetup: VelodromeFixture; let velodromeExchangeAdapter: VelodromeExchangeAdapter; + let sourceAddress: Address; // wbtc + let destinationAddress: Address; // dai + let sourceQuantity: BigNumber; + let destinationQuantity: BigNumber; + before(async () => { [owner, mockSetToken] = await getAccounts(); @@ -43,6 +45,11 @@ describe("VelodromeExchangeAdapter", () => { velodromeExchangeAdapter = await deployer.adapters.deployVelodromeExchangeAdapter( velodromeSetup.router.address, ); + + sourceAddress = setup.wbtc.address; // WBTC Address + sourceQuantity = BigNumber.from(100000000); // Trade 1 WBTC + destinationAddress = setup.dai.address; // DAI Address + destinationQuantity = ether(30000); // Receive at least 30k DAI }); addSnapshotBeforeRestoreAfterEach(); @@ -78,12 +85,73 @@ describe("VelodromeExchangeAdapter", () => { }); }); - describe("generateDataParam / getTradeCalldata", async () => { - let sourceAddress: Address; - let destinationAddress: Address; - let sourceQuantity: BigNumber; - let destinationQuantity: BigNumber; + describe("generateDataParam", async () => { + let subjectData: Bytes; + + it("should revert with empty routes when no routes passed in", async () => { + await expect( + velodromeExchangeAdapter.generateDataParam([], ethers.constants.MaxUint256), + ).to.revertedWith("empty routes"); + }); + + it("should revert with invalid deadline when invalid deadline passed in", async () => { + await expect( + velodromeExchangeAdapter.generateDataParam( + [ + { + from: sourceAddress, + to: destinationAddress, + stable: false, + }, + ], + 0, + ), + ).to.revertedWith("invalid deadline"); + }); + + it("should return the correct data param for wbtc -> dai", async () => { + subjectData = await velodromeExchangeAdapter.generateDataParam( + [ + { + from: sourceAddress, + to: destinationAddress, + stable: false, + }, + ], + ethers.constants.MaxUint256, + ); + expect(subjectData).to.eq( + ethers.utils.defaultAbiCoder.encode( + ["tuple(address,address,bool)[]", "uint256"], + [[[sourceAddress, destinationAddress, false]], ethers.constants.MaxUint256], + ), + ); + }); + + it("should return the correct data param for wbtc -> weth -> dai", async () => { + subjectData = await velodromeExchangeAdapter.generateDataParam( + [ + { from: sourceAddress, to: setup.weth.address, stable: false }, + { from: setup.weth.address, to: destinationAddress, stable: false }, + ], + ethers.constants.MaxUint256, + ); + expect(subjectData).to.eq( + ethers.utils.defaultAbiCoder.encode( + ["tuple(address,address,bool)[]", "uint256"], + [ + [ + [sourceAddress, setup.weth.address, false], + [setup.weth.address, destinationAddress, false], + ], + ethers.constants.MaxUint256, + ], + ), + ); + }); + }); + describe("getTradeCalldata", async () => { let subjectMockSetToken: Address; let subjectSourceToken: Address; let subjectDestinationToken: Address; @@ -92,11 +160,6 @@ describe("VelodromeExchangeAdapter", () => { let subjectData: Bytes; beforeEach(async () => { - sourceAddress = setup.wbtc.address; // WBTC Address - sourceQuantity = BigNumber.from(100000000); // Trade 1 WBTC - destinationAddress = setup.dai.address; // DAI Address - destinationQuantity = ether(30000); // Receive at least 30k DAI - subjectSourceToken = sourceAddress; subjectDestinationToken = destinationAddress; subjectMockSetToken = mockSetToken.address; @@ -115,188 +178,124 @@ describe("VelodromeExchangeAdapter", () => { ); } - describe("should check parameters", () => { - it("should check params of generateDataParam", async () => { - await expect( - velodromeExchangeAdapter.generateDataParam([], ethers.constants.MaxUint256), - ).to.revertedWith("empty routes"); - await expect( - velodromeExchangeAdapter.generateDataParam( - [ - { - from: sourceAddress, - to: destinationAddress, - stable: false, - }, - ], - 0, - ), - ).to.revertedWith("invalid deadline"); - }); - - it("should check params of getTradeCalldata", async () => { - await expect( - velodromeExchangeAdapter.getTradeCalldata( - subjectSourceToken, - subjectDestinationToken, - subjectMockSetToken, - subjectSourceQuantity, - subjectMinDestinationQuantity, - ethers.utils.defaultAbiCoder.encode( - ["tuple(address,address,bool)[]", "uint256"], - [[], ethers.constants.MaxUint256], - ), - ), - ).to.revertedWith("empty routes"); - - await expect( - velodromeExchangeAdapter.getTradeCalldata( - subjectDestinationToken, - subjectDestinationToken, - subjectMockSetToken, - subjectSourceQuantity, - subjectMinDestinationQuantity, - ethers.utils.defaultAbiCoder.encode( - ["tuple(address,address,bool)[]", "uint256"], - [[[sourceAddress, destinationAddress, false]], ethers.constants.MaxUint256], - ), - ), - ).to.revertedWith("Source token path mismatch"); - - await expect( - velodromeExchangeAdapter.getTradeCalldata( - subjectSourceToken, - subjectSourceToken, - subjectMockSetToken, - subjectSourceQuantity, - subjectMinDestinationQuantity, - ethers.utils.defaultAbiCoder.encode( - ["tuple(address,address,bool)[]", "uint256"], - [[[sourceAddress, destinationAddress, false]], ethers.constants.MaxUint256], - ), + it("should revert with empty routes when no routes passed in", async () => { + await expect( + velodromeExchangeAdapter.getTradeCalldata( + subjectSourceToken, + subjectDestinationToken, + subjectMockSetToken, + subjectSourceQuantity, + subjectMinDestinationQuantity, + ethers.utils.defaultAbiCoder.encode( + ["tuple(address,address,bool)[]", "uint256"], + [[], ethers.constants.MaxUint256], ), - ).to.revertedWith("Destination token path mismatch"); + ), + ).to.revertedWith("empty routes"); + }); - await expect( - velodromeExchangeAdapter.getTradeCalldata( - subjectSourceToken, - subjectDestinationToken, - subjectMockSetToken, - subjectSourceQuantity, - subjectMinDestinationQuantity, - ethers.utils.defaultAbiCoder.encode( - ["tuple(address,address,bool)[]", "uint256"], - [[[sourceAddress, destinationAddress, false]], 0], - ), + it("should revert with source token path mismatch", async () => { + await expect( + velodromeExchangeAdapter.getTradeCalldata( + subjectDestinationToken, + subjectDestinationToken, + subjectMockSetToken, + subjectSourceQuantity, + subjectMinDestinationQuantity, + ethers.utils.defaultAbiCoder.encode( + ["tuple(address,address,bool)[]", "uint256"], + [[[sourceAddress, destinationAddress, false]], ethers.constants.MaxUint256], ), - ).to.revertedWith("invalid deadline"); - }); + ), + ).to.revertedWith("Source token path mismatch"); }); - describe("wbtc -> dai", async () => { - it("should return the correct data param", async () => { - subjectData = await velodromeExchangeAdapter.generateDataParam( - [ - { - from: sourceAddress, - to: destinationAddress, - stable: false, - }, - ], - ethers.constants.MaxUint256, - ); - expect(subjectData).to.eq( + it("should revert with destination token path mismatch", async () => { + await expect( + velodromeExchangeAdapter.getTradeCalldata( + subjectSourceToken, + subjectSourceToken, + subjectMockSetToken, + subjectSourceQuantity, + subjectMinDestinationQuantity, ethers.utils.defaultAbiCoder.encode( ["tuple(address,address,bool)[]", "uint256"], [[[sourceAddress, destinationAddress, false]], ethers.constants.MaxUint256], ), - ); - }); - - it("should return the correct trade calldata", async () => { - const callTimestamp = await getLastBlockTimestamp(); - subjectData = await velodromeExchangeAdapter.generateDataParam( - [ - { - from: sourceAddress, - to: destinationAddress, - stable: false, - }, - ], - callTimestamp, - ); - const calldata = await subject(); - const expectedCallData = velodromeSetup.router.interface.encodeFunctionData( - "swapExactTokensForTokens", - [ - sourceQuantity, - destinationQuantity, - [{ from: sourceAddress, to: destinationAddress, stable: false }], - subjectMockSetToken, - callTimestamp, - ], - ); - expect(JSON.stringify(calldata)).to.eq( - JSON.stringify([velodromeSetup.router.address, ZERO, expectedCallData]), - ); - }); + ), + ).to.revertedWith("Destination token path mismatch"); }); - describe("wbtc -> weth -> dai", async () => { - beforeEach(async () => { - const path = [sourceAddress, setup.weth.address, destinationAddress]; - subjectData = utils.defaultAbiCoder.encode(["address[]"], [path]); - }); - - it("should return the correct data param", async () => { - subjectData = await velodromeExchangeAdapter.generateDataParam( - [ - { from: sourceAddress, to: setup.weth.address, stable: false }, - { from: setup.weth.address, to: destinationAddress, stable: false }, - ], - ethers.constants.MaxUint256, - ); - expect(subjectData).to.eq( + it("should revert with invalid deadline when invalid deadline passed in", async () => { + await expect( + velodromeExchangeAdapter.getTradeCalldata( + subjectSourceToken, + subjectDestinationToken, + subjectMockSetToken, + subjectSourceQuantity, + subjectMinDestinationQuantity, ethers.utils.defaultAbiCoder.encode( ["tuple(address,address,bool)[]", "uint256"], - [ - [ - [sourceAddress, setup.weth.address, false], - [setup.weth.address, destinationAddress, false], - ], - ethers.constants.MaxUint256, - ], + [[[sourceAddress, destinationAddress, false]], 0], ), - ); - }); + ), + ).to.revertedWith("invalid deadline"); + }); - it("should return the correct trade calldata", async () => { - const callTimestamp = await getLastBlockTimestamp(); - subjectData = await velodromeExchangeAdapter.generateDataParam( + it("should return the correct trade calldata for wbtc -> dai", async () => { + const callTimestamp = await getLastBlockTimestamp(); + subjectData = await velodromeExchangeAdapter.generateDataParam( + [ + { + from: sourceAddress, + to: destinationAddress, + stable: false, + }, + ], + callTimestamp, + ); + const calldata = await subject(); + const expectedCallData = velodromeSetup.router.interface.encodeFunctionData( + "swapExactTokensForTokens", + [ + sourceQuantity, + destinationQuantity, + [{ from: sourceAddress, to: destinationAddress, stable: false }], + subjectMockSetToken, + callTimestamp, + ], + ); + expect(JSON.stringify(calldata)).to.eq( + JSON.stringify([velodromeSetup.router.address, ZERO, expectedCallData]), + ); + }); + + it("should return the correct trade calldata for wbtc -> wetb -> dai", async () => { + const callTimestamp = await getLastBlockTimestamp(); + subjectData = await velodromeExchangeAdapter.generateDataParam( + [ + { from: sourceAddress, to: setup.weth.address, stable: false }, + { from: setup.weth.address, to: destinationAddress, stable: false }, + ], + callTimestamp, + ); + const calldata = await subject(); + const expectedCallData = velodromeSetup.router.interface.encodeFunctionData( + "swapExactTokensForTokens", + [ + sourceQuantity, + destinationQuantity, [ { from: sourceAddress, to: setup.weth.address, stable: false }, { from: setup.weth.address, to: destinationAddress, stable: false }, ], + subjectMockSetToken, callTimestamp, - ); - const calldata = await subject(); - const expectedCallData = velodromeSetup.router.interface.encodeFunctionData( - "swapExactTokensForTokens", - [ - sourceQuantity, - destinationQuantity, - [ - { from: sourceAddress, to: setup.weth.address, stable: false }, - { from: setup.weth.address, to: destinationAddress, stable: false }, - ], - subjectMockSetToken, - callTimestamp, - ], - ); - expect(JSON.stringify(calldata)).to.eq( - JSON.stringify([velodromeSetup.router.address, ZERO, expectedCallData]), - ); - }); + ], + ); + expect(JSON.stringify(calldata)).to.eq( + JSON.stringify([velodromeSetup.router.address, ZERO, expectedCallData]), + ); }); }); }); diff --git a/utils/contracts/velodrome.ts b/utils/contracts/velodrome.ts index 88d3a722d..c716246f8 100644 --- a/utils/contracts/velodrome.ts +++ b/utils/contracts/velodrome.ts @@ -1,8 +1,2 @@ -// External Uniswap Contracts -export { StakingRewards } from "../../typechain/StakingRewards"; -export { Uni } from "../../typechain/Uni"; -export { UniswapGovernorAlpha } from "../../typechain/UniswapGovernorAlpha"; -export { UniswapTimelock } from "../../typechain/UniswapTimelock"; -export { VelodromeFactory } from "../../typechain/VelodromeFactory"; -export { UniswapV2Pair } from "../../typechain/UniswapV2Pair"; -export { UniswapV2Router02 } from "../../typechain/UniswapV2Router02"; \ No newline at end of file +// External Velodrome Contracts +export { VelodromeFactory } from "../../typechain/VelodromeFactory"; \ No newline at end of file diff --git a/utils/fixtures/index.ts b/utils/fixtures/index.ts index e390db2c4..754f278d4 100644 --- a/utils/fixtures/index.ts +++ b/utils/fixtures/index.ts @@ -8,3 +8,4 @@ export { UniswapFixture } from "./uniswapFixture"; export { UniswapV3Fixture } from "./uniswapV3Fixture"; export { YearnFixture } from "./yearnFixture"; export { PerpV2Fixture } from "./perpV2Fixture"; +export { VelodromeFixture } from "./velodromeFixture"; diff --git a/utils/fixtures/velodromeFixture.ts b/utils/fixtures/velodromeFixture.ts index 4989f7f8c..7aed9f051 100644 --- a/utils/fixtures/velodromeFixture.ts +++ b/utils/fixtures/velodromeFixture.ts @@ -4,11 +4,11 @@ import { Address } from "../types"; import { Account } from "../test/types"; import dependencies from "../deploys/dependencies"; -import { Uni } from "@typechain/Uni"; -import { VelodromeFactory } from "@typechain/VelodromeFactory"; -import { VelodromePair } from "@typechain/VelodromePair"; -import { VelodromeRouter } from "@typechain/VelodromeRouter"; -import { VelodromePair__factory } from "@typechain/factories/VelodromePair__factory"; +import { Uni } from "../../typechain/Uni"; +import { VelodromeFactory } from "../../typechain/VelodromeFactory"; +import { VelodromePair } from "../../typechain/VelodromePair"; +import { VelodromeRouter } from "../../typechain/VelodromeRouter"; +import { VelodromePair__factory } from "../../typechain/factories/VelodromePair__factory"; export class VelodromeFixture { private _deployer: DeployHelper; diff --git a/utils/test/index.ts b/utils/test/index.ts index 50cb9d2d3..067531ae1 100644 --- a/utils/test/index.ts +++ b/utils/test/index.ts @@ -12,10 +12,10 @@ import { UniswapFixture, YearnFixture, UniswapV3Fixture, - PerpV2Fixture + PerpV2Fixture, + VelodromeFixture } from "../fixtures"; import { Blockchain, ProtocolUtils } from "../common"; -import { VelodromeFixture } from "@utils/fixtures/velodromeFixture"; // Hardhat-Provider Aware Exports const provider = ethers.provider; From 5d637c0c88c06768ba6c80577a8f6255f9ce556b Mon Sep 17 00:00:00 2001 From: deephil0226 Date: Thu, 30 Jun 2022 08:46:10 -0400 Subject: [PATCH 10/10] fix comments --- .../integration/exchange/velodromeExchangeAdapter.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts b/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts index b2fd66192..2f87c1c5c 100644 --- a/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts +++ b/test/protocol/integration/exchange/velodromeExchangeAdapter.spec.ts @@ -54,7 +54,7 @@ describe("VelodromeExchangeAdapter", () => { addSnapshotBeforeRestoreAfterEach(); - describe("constructor", async () => { + describe("#constructor", async () => { let subjectVelodromeRouter: Address; beforeEach(async () => { @@ -73,7 +73,7 @@ describe("VelodromeExchangeAdapter", () => { }); }); - describe("getSpender", async () => { + describe("#getSpender", async () => { async function subject(): Promise { return await velodromeExchangeAdapter.getSpender(); } @@ -85,7 +85,7 @@ describe("VelodromeExchangeAdapter", () => { }); }); - describe("generateDataParam", async () => { + describe("#generateDataParam", async () => { let subjectData: Bytes; it("should revert with empty routes when no routes passed in", async () => { @@ -151,7 +151,7 @@ describe("VelodromeExchangeAdapter", () => { }); }); - describe("getTradeCalldata", async () => { + describe("#getTradeCalldata", async () => { let subjectMockSetToken: Address; let subjectSourceToken: Address; let subjectDestinationToken: Address;