Overview
S Balance
S Value
$0.00More Info
Private Name Tags
ContractCreator
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
GatewayImplementation
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import '../vault/IVault.sol'; import './IGateway.sol'; import '../token/IDToken.sol'; import '../token/IIOU.sol'; import '../../oracle/IOracle.sol'; import '../swapper/ISwapper.sol'; import '../../library/Bytes32Map.sol'; import '../../library/ETHAndERC20.sol'; import '../../library/SafeMath.sol'; import { GatewayIndex as I } from './GatewayIndex.sol'; import './GatewayHelper.sol'; import './GatewayStorage.sol'; import '../../utils/ISwitchOracle.sol'; contract GatewayImplementation is GatewayStorage { using Bytes32Map for mapping(uint8 => bytes32); using ETHAndERC20 for address; using SafeMath for uint256; using SafeMath for int256; error InvalidBToken(); error InvalidBAmount(); error InvalidBPrice(); error InvalidLTokenId(); error InvalidPTokenId(); error InvalidRequestId(); error InsufficientMargin(); error InsufficientB0(); event RequestUpdateLiquidity( uint256 requestId, uint256 lTokenId, uint256 liquidity, int256 lastCumulativePnlOnEngine, int256 cumulativePnlOnGateway, uint256 removeBAmount ); event RequestRemoveMargin( uint256 requestId, uint256 pTokenId, uint256 realMoneyMargin, int256 lastCumulativePnlOnEngine, int256 cumulativePnlOnGateway, uint256 bAmount ); event RequestTrade( uint256 requestId, uint256 pTokenId, uint256 realMoneyMargin, int256 lastCumulativePnlOnEngine, int256 cumulativePnlOnGateway, bytes32 symbolId, int256[] tradeParams ); event RequestLiquidate( uint256 requestId, uint256 pTokenId, uint256 realMoneyMargin, int256 lastCumulativePnlOnEngine, int256 cumulativePnlOnGateway ); event RequestTradeAndRemoveMargin( uint256 requestId, uint256 pTokenId, uint256 realMoneyMargin, int256 lastCumulativePnlOnEngine, int256 cumulativePnlOnGateway, uint256 bAmount, bytes32 symbolId, int256[] tradeParams ); event FinishAddLiquidity( uint256 requestId, uint256 lTokenId, uint256 liquidity, uint256 totalLiquidity ); event FinishRemoveLiquidity( uint256 requestId, uint256 lTokenId, uint256 liquidity, uint256 totalLiquidity, address bToken, uint256 bAmount ); event FinishAddMargin( uint256 requestId, uint256 pTokenId, address bToken, uint256 bAmount ); event FinishRemoveMargin( uint256 requestId, uint256 pTokenId, address bToken, uint256 bAmount ); event FinishLiquidate( uint256 requestId, uint256 pTokenId, int256 lpPnl ); uint256 constant UONE = 1e18; int256 constant ONE = 1e18; address constant tokenETH = address(1); IDToken internal immutable lToken; IDToken internal immutable pToken; IOracle internal immutable oracle; ISwapper internal immutable swapper; IVault internal immutable vault0; // Vault for holding reserved B0, used for payments on regular bases IIOU internal immutable iou; // IOU ERC20, issued to traders when B0 insufficent address internal immutable tokenB0; // B0, settlement base token, e.g. USDC address internal immutable dChainEventSigner; uint8 internal immutable decimalsB0; uint256 internal immutable b0ReserveRatio; int256 internal immutable liquidationRewardCutRatio; int256 internal immutable minLiquidationReward; int256 internal immutable maxLiquidationReward; address internal immutable protocolFeeManager; address internal immutable liqClaim; address internal immutable switchOracle; constructor (IGateway.GatewayParam memory p) { lToken = IDToken(p.lToken); pToken = IDToken(p.pToken); oracle = IOracle(p.oracle); swapper = ISwapper(p.swapper); vault0 = IVault(p.vault0); iou = IIOU(p.iou); tokenB0 = p.tokenB0; decimalsB0 = p.tokenB0.decimals(); dChainEventSigner = p.dChainEventSigner; b0ReserveRatio = p.b0ReserveRatio; liquidationRewardCutRatio = p.liquidationRewardCutRatio; minLiquidationReward = p.minLiquidationReward; maxLiquidationReward = p.maxLiquidationReward; protocolFeeManager = p.protocolFeeManager; liqClaim = p.liqClaim; switchOracle = p.switchOracle; } //================================================================================ // Getters //================================================================================ function getGatewayParam() external view returns (IGateway.GatewayParam memory p) { p.lToken = address(lToken); p.pToken = address(pToken); p.oracle = address(oracle); p.swapper = address(swapper); p.vault0 = address(vault0); p.iou = address(iou); p.tokenB0 = tokenB0; p.dChainEventSigner = dChainEventSigner; p.b0ReserveRatio = b0ReserveRatio; p.liquidationRewardCutRatio = liquidationRewardCutRatio; p.minLiquidationReward = minLiquidationReward; p.maxLiquidationReward = maxLiquidationReward; p.protocolFeeManager = protocolFeeManager; p.liqClaim = liqClaim; } function getGatewayState() external view returns (IGateway.GatewayState memory s) { return GatewayHelper.getGatewayState(_gatewayStates); } function getBTokenState(address bToken) external view returns (IGateway.BTokenState memory s) { return GatewayHelper.getBTokenState(_bTokenStates, bToken); } function getLpState(uint256 lTokenId) external view returns (IGateway.LpState memory s) { return GatewayHelper.getLpState(_bTokenStates, _dTokenStates, lTokenId); } function getTdState(uint256 pTokenId) external view returns (IGateway.TdState memory s) { return GatewayHelper.getTdState(_bTokenStates, _dTokenStates, pTokenId); } // @notice Calculate Lp's cumulative time, used in liquidity mining reward distributions function getCumulativeTime(uint256 lTokenId) public view returns (uint256 cumulativeTimePerLiquidity, uint256 cumulativeTime) { return GatewayHelper.getCumulativeTime(_gatewayStates, _dTokenStates, lTokenId); } function getExecutionFees() public view returns (uint256[] memory fees) { return GatewayHelper.getExecutionFees(_executionFees); } //================================================================================ // Setters //================================================================================ // function addBToken( // address bToken, // address vault, // bytes32 oracleId, // uint256 collateralFactor // ) external _onlyAdmin_ { // GatewayHelper.addBToken( // _bTokenStates, // swapper, // oracle, // vault0, // tokenB0, // bToken, // vault, // oracleId, // collateralFactor // ); // } // function delBToken(address bToken) external _onlyAdmin_ { // GatewayHelper.delBToken(_bTokenStates, bToken); // } // // @dev This function can be used to change bToken collateral factor // function setBTokenParameter(address bToken, uint8 idx, bytes32 value) external _onlyAdmin_ { // GatewayHelper.setBTokenParameter(_bTokenStates, bToken, idx, value); // } // // @notice Set execution fee for actionId // function setExecutionFee(uint256 actionId, uint256 executionFee) external _onlyAdmin_ { // GatewayHelper.setExecutionFee(_executionFees, actionId, executionFee); // } // function setDChainExecutionFeePerRequest(uint256 dChainExecutionFeePerRequest) external _onlyAdmin_ { // GatewayHelper.setDChainExecutionFeePerRequest(_gatewayStates, dChainExecutionFeePerRequest); // } // @notic Claim dChain executionFee to account `to` function claimDChainExecutionFee(address to) external _onlyAdmin_ { GatewayHelper.claimDChainExecutionFee(_gatewayStates, to); } // @notice Claim unused iChain execution fee for dTokenId function claimUnusedIChainExecutionFee(uint256 dTokenId, bool isLp) external { GatewayHelper.claimUnusedIChainExecutionFee( _gatewayStates, _dTokenStates, lToken, pToken, dTokenId, isLp ); } // @notice Redeem B0 for burning IOU function redeemIOU(uint256 b0Amount) external { GatewayHelper.redeemIOU(tokenB0, vault0, iou, msg.sender, b0Amount); } //================================================================================ // Interactions //================================================================================ function finishCollectProtocolFee(bytes memory eventData, bytes memory signature) external _onlyAdmin_ { GatewayHelper.verifyEventData(eventData, signature, 64, dChainEventSigner); IGateway.VarOnExecuteCollectProtocolFee memory v = abi.decode(eventData, (IGateway.VarOnExecuteCollectProtocolFee)); require(v.chainId == block.chainid); GatewayHelper.finishCollectProtocolFee( _gatewayStates, vault0, tokenB0, protocolFeeManager, v.cumulativeCollectedProtocolFeeOnEngine ); } /** * @notice Request to add liquidity with specified base token. * @param lTokenId The unique identifier of the LToken. * @param bToken The address of the base token to add as liquidity. * @param bAmount The amount of base tokens to add as liquidity. */ function requestAddLiquidity(uint256 lTokenId, address bToken, uint256 bAmount) external payable { if (lTokenId == 0) { lTokenId = lToken.mint(msg.sender); } else { _checkLTokenIdOwner(lTokenId, msg.sender); } _checkBTokenInitialized(bToken); Data memory data = _getDataAndCheckBTokenConsistency(msg.sender, lTokenId, bToken); uint256 ethAmount = _receiveExecutionFee(lTokenId, _executionFees[I.ACTION_REQUESTADDLIQUIDITY]); if (bToken == tokenETH) { bAmount = ethAmount; } if (bAmount == 0) { revert InvalidBAmount(); } if (bToken != tokenETH) { bToken.transferIn(data.account, bAmount); } _deposit(data, bAmount); _getExParams(data); uint256 newLiquidity = _getDTokenLiquidity(data); _saveData(data); uint256 requestId = _incrementRequestId(lTokenId); emit RequestUpdateLiquidity( requestId, lTokenId, newLiquidity, data.lastCumulativePnlOnEngine, data.cumulativePnlOnGateway, 0 ); } /** * @notice Request to remove liquidity with specified base token. * @param lTokenId The unique identifier of the LToken. * @param bToken The address of the base token to remove as liquidity. * @param bAmount The amount of base tokens to remove as liquidity. */ function requestRemoveLiquidity(uint256 lTokenId, address bToken, uint256 bAmount) external payable { _checkLTokenIdOwner(lTokenId, msg.sender); _receiveExecutionFee(lTokenId, _executionFees[I.ACTION_REQUESTREMOVELIQUIDITY]); if (bAmount == 0) { revert InvalidBAmount(); } Data memory data = _getData(msg.sender, lTokenId, _dTokenStates[lTokenId].getAddress(I.D_BTOKEN)); _getExParams(data); uint256 oldLiquidity = _getDTokenLiquidity(data); uint256 newLiquidity; if (data.bToken == bToken) { newLiquidity = _getDTokenLiquidityWithRemove(data, bAmount); } else if (bToken == tokenB0) { newLiquidity = _getDTokenLiquidityWithRemoveB0(data, bAmount); } else { revert InvalidBToken(); } if (newLiquidity <= oldLiquidity / 100) { newLiquidity = 0; } _dTokenStates[lTokenId].set(I.D_CURRENTOPERATETOKEN, bToken); uint256 requestId = _incrementRequestId(lTokenId); emit RequestUpdateLiquidity( requestId, lTokenId, newLiquidity, data.lastCumulativePnlOnEngine, data.cumulativePnlOnGateway, bAmount ); } /** * @notice Request to add margin with specified base token. * @param pTokenId The unique identifier of the PToken. * @param bToken The address of the base token to add as margin. * @param bAmount The amount of base tokens to add as margin. * @param singlePosition The flag whether trader is using singlePosition margin. * @return The unique identifier pTokenId. */ function requestAddMargin(uint256 pTokenId, address bToken, uint256 bAmount, bool singlePosition) public payable returns (uint256) { if (pTokenId == 0) { pTokenId = pToken.mint(msg.sender); if (singlePosition) { _dTokenStates[pTokenId].set(I.D_SINGLEPOSITION, true); } } else { _checkPTokenIdOwner(pTokenId, msg.sender); } _checkBTokenInitialized(bToken); Data memory data = _getDataAndCheckBTokenConsistency(msg.sender, pTokenId, bToken); if (bToken == tokenETH) { if (bAmount > msg.value) { revert InvalidBAmount(); } } if (bAmount == 0) { revert InvalidBAmount(); } if (bToken != tokenETH) { bToken.transferIn(data.account, bAmount); } _deposit(data, bAmount); _saveData(data); uint256 requestId = _incrementRequestId(pTokenId); emit FinishAddMargin( requestId, pTokenId, bToken, bAmount ); return pTokenId; } function requestAddMarginB0(uint256 pTokenId, uint256 b0Amount) external { _checkPTokenIdOwner(pTokenId, msg.sender); tokenB0.transferIn(msg.sender, b0Amount); vault0.deposit(uint256(0), b0Amount); int256 curB0Amount = _dTokenStates[pTokenId].getInt(I.D_B0AMOUNT); _dTokenStates[pTokenId].set(I.D_B0AMOUNT, curB0Amount + b0Amount.utoi()); } /** * @notice Request to remove margin with specified base token. * @param pTokenId The unique identifier of the PToken. * @param bToken The address of the base token to remove as margin. * @param bAmount The amount of base tokens to remove as margin. */ function requestRemoveMargin(uint256 pTokenId, address bToken, uint256 bAmount) external payable { _checkPTokenIdOwner(pTokenId, msg.sender); _receiveExecutionFee(pTokenId, _executionFees[I.ACTION_REQUESTREMOVEMARGIN]); if (bAmount == 0) { revert InvalidBAmount(); } Data memory data = _getDataAndCheckBTokenConsistency(msg.sender, pTokenId, bToken); _getExParams(data); uint256 oldMargin = _getDTokenLiquidity(data); uint256 newMargin = _getDTokenLiquidityWithRemove(data, bAmount); if (newMargin <= oldMargin / 100) { newMargin = 0; } uint256 requestId = _incrementRequestId(pTokenId); emit RequestRemoveMargin( requestId, pTokenId, newMargin, data.lastCumulativePnlOnEngine, data.cumulativePnlOnGateway, bAmount ); } /** * @notice Request to initiate a trade using a specified PToken, symbol identifier, and trade parameters. * @param pTokenId The unique identifier of the PToken. * @param symbolId The identifier of the trading symbol. * @param tradeParams An array of trade parameters for the trade execution. */ function requestTrade(uint256 pTokenId, bytes32 symbolId, int256[] calldata tradeParams) public payable { _checkPTokenIdOwner(pTokenId, msg.sender); _receiveExecutionFee(pTokenId, _executionFees[I.ACTION_REQUESTTRADE]); Data memory data = _getDataAndCheckBTokenConsistency(msg.sender, pTokenId, _dTokenStates[pTokenId].getAddress(I.D_BTOKEN)); _getExParams(data); uint256 realMoneyMargin = _getDTokenLiquidity(data); uint256 requestId = _incrementRequestId(pTokenId); emit RequestTrade( requestId, pTokenId, realMoneyMargin, data.lastCumulativePnlOnEngine, data.cumulativePnlOnGateway, symbolId, tradeParams ); } /** * @notice Request to liquidate a specified PToken. * @param pTokenId The unique identifier of the PToken. */ function requestLiquidate(uint256 pTokenId) external { Data memory data = _getDataAndCheckBTokenConsistency(pToken.ownerOf(pTokenId), pTokenId, _dTokenStates[pTokenId].getAddress(I.D_BTOKEN)); _getExParams(data); uint256 realMoneyMargin = _getDTokenLiquidity(data); uint256 requestId = _incrementRequestId(pTokenId); emit RequestLiquidate( requestId, pTokenId, realMoneyMargin, data.lastCumulativePnlOnEngine, data.cumulativePnlOnGateway ); } /** * @notice Request to add margin and initiate a trade in a single transaction. * @param pTokenId The unique identifier of the PToken. * @param bToken The address of the base token to add as margin. * @param bAmount The amount of base tokens to add as margin. * @param symbolId The identifier of the trading symbol for the trade. * @param tradeParams An array of trade parameters for the trade execution. * @param singlePosition The flag whether trader is using singlePosition margin. */ function requestAddMarginAndTrade( uint256 pTokenId, address bToken, uint256 bAmount, bytes32 symbolId, int256[] calldata tradeParams, bool singlePosition ) external payable { if (bToken == tokenETH) { uint256 executionFee = _executionFees[I.ACTION_REQUESTTRADE]; if (bAmount + executionFee > msg.value) { // revert if bAmount > msg.value - executionFee revert InvalidBAmount(); } } pTokenId = requestAddMargin(pTokenId, bToken, bAmount, singlePosition); requestTrade(pTokenId, symbolId, tradeParams); } /** * @notice Request to initiate a trade and simultaneously remove margin from a specified PToken. * @param pTokenId The unique identifier of the PToken. * @param bToken The address of the base token to remove as margin. * @param bAmount The amount of base tokens to remove as margin. * @param symbolId The identifier of the trading symbol for the trade. * @param tradeParams An array of trade parameters for the trade execution. */ function requestTradeAndRemoveMargin( uint256 pTokenId, address bToken, uint256 bAmount, bytes32 symbolId, int256[] calldata tradeParams ) external payable { _checkPTokenIdOwner(pTokenId, msg.sender); _receiveExecutionFee(pTokenId, _executionFees[I.ACTION_REQUESTTRADEANDREMOVEMARGIN]); if (bAmount == 0) { revert InvalidBAmount(); } Data memory data = _getDataAndCheckBTokenConsistency(msg.sender, pTokenId, bToken); _getExParams(data); uint256 oldMargin = _getDTokenLiquidity(data); uint256 newMargin = _getDTokenLiquidityWithRemove(data, bAmount); if (newMargin <= oldMargin / 100) { newMargin = 0; } uint256 requestId = _incrementRequestId(pTokenId); emit RequestTradeAndRemoveMargin( requestId, pTokenId, newMargin, data.lastCumulativePnlOnEngine, data.cumulativePnlOnGateway, bAmount, symbolId, tradeParams ); } /** * @notice Finalize the liquidity update based on event emitted on d-chain. * @param eventData The encoded event data containing information about the liquidity update, emitted on d-chain. * @param signature The signature used to verify the event data. */ function finishUpdateLiquidity(bytes memory eventData, bytes memory signature) external _reentryLock_ { GatewayHelper.verifyEventData(eventData, signature, 192, dChainEventSigner); IGateway.VarOnExecuteUpdateLiquidity memory v = abi.decode(eventData, (IGateway.VarOnExecuteUpdateLiquidity)); _checkRequestId(v.lTokenId, v.requestId); _updateLiquidity(v.lTokenId, v.liquidity, v.totalLiquidity); // Cumulate unsettled PNL to b0Amount Data memory data = _getDataAndCheckBTokenConsistency(lToken.ownerOf(v.lTokenId), v.lTokenId, _dTokenStates[v.lTokenId].getAddress(I.D_BTOKEN)); int256 diff = v.cumulativePnlOnEngine.minusUnchecked(data.lastCumulativePnlOnEngine); data.b0Amount += diff.rescaleDown(18, decimalsB0); data.lastCumulativePnlOnEngine = v.cumulativePnlOnEngine; uint256 bAmountRemoved; address operateToken = data.bToken; if (v.bAmountToRemove != 0) { operateToken = _dTokenStates[v.lTokenId].getAddress(I.D_CURRENTOPERATETOKEN); if (data.bToken == operateToken) { _getExParams(data); bAmountRemoved = _transferOut(data, v.liquidity == 0 ? type(uint256).max : v.bAmountToRemove, false); } else { require(operateToken == tokenB0); if (data.b0Amount > 0) { bAmountRemoved = vault0.redeem(uint256(0), SafeMath.min(v.bAmountToRemove, data.b0Amount.itou())); data.b0Amount -= bAmountRemoved.utoi(); tokenB0.transferOut(data.account, bAmountRemoved); } } } _saveData(data); _transferLastRequestIChainExecutionFee(v.lTokenId, msg.sender); if (v.bAmountToRemove == 0) { // If bAmountToRemove == 0, it is a AddLiqudiity finalization emit FinishAddLiquidity( v.requestId, v.lTokenId, v.liquidity, v.totalLiquidity ); } else { // If bAmountToRemove != 0, it is a RemoveLiquidity finalization emit FinishRemoveLiquidity( v.requestId, v.lTokenId, v.liquidity, v.totalLiquidity, operateToken, bAmountRemoved ); } } /** * @notice Finalize the remove of margin based on event emitted on d-chain. * @param eventData The encoded event data containing information about the margin remove, emitted on d-chain. * @param signature The signature used to verify the event data. */ function finishRemoveMargin(bytes memory eventData, bytes memory signature) external _reentryLock_ { GatewayHelper.verifyEventData(eventData, signature, 160, dChainEventSigner); IGateway.VarOnExecuteRemoveMargin memory v = abi.decode(eventData, (IGateway.VarOnExecuteRemoveMargin)); _checkRequestId(v.pTokenId, v.requestId); // Cumulate unsettled PNL to b0Amount Data memory data = _getDataAndCheckBTokenConsistency(pToken.ownerOf(v.pTokenId), v.pTokenId, _dTokenStates[v.pTokenId].getAddress(I.D_BTOKEN)); int256 diff = v.cumulativePnlOnEngine.minusUnchecked(data.lastCumulativePnlOnEngine); data.b0Amount += diff.rescaleDown(18, decimalsB0); data.lastCumulativePnlOnEngine = v.cumulativePnlOnEngine; _getExParams(data); uint256 bAmount = _transferOut(data, v.bAmountToRemove, true); if (_getDTokenLiquidity(data) < v.requiredMargin) { revert InsufficientMargin(); } _saveData(data); _transferLastRequestIChainExecutionFee(v.pTokenId, msg.sender); emit FinishRemoveMargin( v.requestId, v.pTokenId, data.bToken, bAmount ); } /** * @notice Finalize the liquidation based on event emitted on d-chain. * @param eventData The encoded event data containing information about the liquidation, emitted on d-chain. * @param signature The signature used to verify the event data. */ function finishLiquidate(bytes memory eventData, bytes memory signature) external _reentryLock_ { GatewayHelper.verifyEventData(eventData, signature, 224, dChainEventSigner); IGateway.VarOnExecuteLiquidate memory v = abi.decode(eventData, (IGateway.VarOnExecuteLiquidate)); // Cumulate unsettled PNL to b0Amount Data memory data = _getDataAndCheckBTokenConsistency(pToken.ownerOf(v.pTokenId), v.pTokenId, _dTokenStates[v.pTokenId].getAddress(I.D_BTOKEN)); int256 diff = v.cumulativePnlOnEngine.minusUnchecked(data.lastCumulativePnlOnEngine); data.b0Amount += diff.rescaleDown(18, decimalsB0); data.lastCumulativePnlOnEngine = v.cumulativePnlOnEngine; uint256 b0AmountIn; { uint256 bAmount = IVault(data.vault).redeem(data.dTokenId, type(uint256).max); if (data.bToken == tokenB0) { b0AmountIn += bAmount; } else { b0AmountIn += GatewayHelper.liquidateRedeemAndSwap( decimalsB0, data.bToken, address(swapper), liqClaim, address(pToken), data.dTokenId, data.b0Amount, bAmount, v.maintenanceMarginRequired ); } } int256 lpPnl = b0AmountIn.utoi() + data.b0Amount; // All Lp's PNL by liquidating this trader int256 reward; // Calculate liquidator's reward reward = GatewayHelper.calculateReward(lpPnl, minLiquidationReward, maxLiquidationReward, liquidationRewardCutRatio); (reward, b0AmountIn) = GatewayHelper.processReward(tokenB0, vault0, reward, b0AmountIn, v.executor, v.finisher); lpPnl -= reward; if (b0AmountIn > 0) { vault0.deposit(uint256(0), b0AmountIn); } // Cumulate lpPnl into cumulativePnlOnGateway, // which will be distributed to all LPs on all i-chains with next request process data.cumulativePnlOnGateway = data.cumulativePnlOnGateway.addUnchecked(lpPnl.rescale(decimalsB0, 18)); data.b0Amount = 0; _saveData(data); { uint256 lastRequestIChainExecutionFee = _dTokenStates[v.pTokenId].getUint(I.D_LASTREQUESTICHAINEXECUTIONFEE); uint256 cumulativeUnusedIChainExecutionFee = _dTokenStates[v.pTokenId].getUint(I.D_CUMULATIVEUNUSEDICHAINEXECUTIONFEE); _dTokenStates[v.pTokenId].del(I.D_LASTREQUESTICHAINEXECUTIONFEE); _dTokenStates[v.pTokenId].del(I.D_CUMULATIVEUNUSEDICHAINEXECUTIONFEE); uint256 totalIChainExecutionFee = _gatewayStates.getUint(I.S_TOTALICHAINEXECUTIONFEE); totalIChainExecutionFee -= lastRequestIChainExecutionFee + cumulativeUnusedIChainExecutionFee; _gatewayStates.set(I.S_TOTALICHAINEXECUTIONFEE, totalIChainExecutionFee); } pToken.burn(v.pTokenId); emit FinishLiquidate( v.requestId, v.pTokenId, lpPnl ); } //================================================================================ // Internals //================================================================================ // Temporary struct holding intermediate values passed around functions struct Data { address account; // Lp/Trader account address uint256 dTokenId; // Lp/Trader dTokenId address bToken; // Lp/Trader bToken address int256 cumulativePnlOnGateway; // cumulative pnl on Gateway address vault; // Lp/Trader bToken's vault address int256 b0Amount; // Lp/Trader b0Amount int256 lastCumulativePnlOnEngine; // Lp/Trader last cumulative pnl on engine uint256 collateralFactor; // bToken collateral factor uint256 bPrice; // bToken price } function _getData(address account, uint256 dTokenId, address bToken) internal view returns (Data memory data) { data.account = account; data.dTokenId = dTokenId; data.bToken = bToken; data.cumulativePnlOnGateway = _gatewayStates.getInt(I.S_CUMULATIVEPNLONGATEWAY); data.vault = _bTokenStates[bToken].getAddress(I.B_VAULT); data.b0Amount = _dTokenStates[dTokenId].getInt(I.D_B0AMOUNT); data.lastCumulativePnlOnEngine = _dTokenStates[dTokenId].getInt(I.D_LASTCUMULATIVEPNLONENGINE); } function _getDataAndCheckBTokenConsistency(address account, uint256 dTokenId, address bToken) internal view returns (Data memory data) { data = _getData(account, dTokenId, bToken); _checkBTokenConsistency(dTokenId, bToken); } function _saveData(Data memory data) internal { _gatewayStates.set(I.S_CUMULATIVEPNLONGATEWAY, data.cumulativePnlOnGateway); _dTokenStates[data.dTokenId].set(I.D_BTOKEN, data.bToken); _dTokenStates[data.dTokenId].set(I.D_B0AMOUNT, data.b0Amount); _dTokenStates[data.dTokenId].set(I.D_LASTCUMULATIVEPNLONENGINE, data.lastCumulativePnlOnEngine); } // @notice Check callback's requestId is the same as the current requestId stored for user // If a new request is submitted before the callback for last request, requestId will not match, // and this callback cannot be executed anymore function _checkRequestId(uint256 dTokenId, uint256 requestId) internal { uint128 userRequestId = uint128(requestId); if (_dTokenStates[dTokenId].getUint(I.D_REQUESTID) != uint256(userRequestId)) { revert InvalidRequestId(); } else { // increment requestId so that callback can only be executed once _dTokenStates[dTokenId].set(I.D_REQUESTID, uint256(userRequestId + 1)); } } // @notice Increment gateway requestId and user requestId // and returns the combined requestId for this request // The combined requestId contains 2 parts: // * Lower 128 bits stores user's requestId, only increments when request is from this user // * Higher 128 bits stores gateways's requestId, increments for all new requests in this contract function _incrementRequestId(uint256 dTokenId) internal returns (uint256) { uint128 gatewayRequestId = uint128(_gatewayStates.getUint(I.S_GATEWAYREQUESTID)); gatewayRequestId += 1; _gatewayStates.set(I.S_GATEWAYREQUESTID, uint256(gatewayRequestId)); uint128 userRequestId = uint128(_dTokenStates[dTokenId].getUint(I.D_REQUESTID)); userRequestId += 1; _dTokenStates[dTokenId].set(I.D_REQUESTID, uint256(userRequestId)); uint256 requestId = (uint256(gatewayRequestId) << 128) + uint256(userRequestId); return requestId; } function _checkBTokenInitialized(address bToken) internal view { if (_bTokenStates[bToken].getAddress(I.B_VAULT) == address(0)) { revert InvalidBToken(); } } function _checkBTokenConsistency(uint256 dTokenId, address bToken) internal view { address preBToken = _dTokenStates[dTokenId].getAddress(I.D_BTOKEN); if (preBToken != address(0) && preBToken != bToken) { uint256 stAmount = IVault(_bTokenStates[preBToken].getAddress(I.B_VAULT)).stAmounts(dTokenId); if (stAmount != 0) { revert InvalidBToken(); } } } function _checkLTokenIdOwner(uint256 lTokenId, address owner) internal view { if (lToken.ownerOf(lTokenId) != owner) { revert InvalidLTokenId(); } } function _checkPTokenIdOwner(uint256 pTokenId, address owner) internal view { if (pToken.ownerOf(pTokenId) != owner) { revert InvalidPTokenId(); } } function _receiveExecutionFee(uint256 dTokenId, uint256 executionFee) internal returns (uint256) { return GatewayHelper.receiveExecutionFee(_gatewayStates, _dTokenStates, dTokenId, executionFee); } function _transferLastRequestIChainExecutionFee(uint256 dTokenId, address to) internal { GatewayHelper.transferLastRequestIChainExecutionFee(_gatewayStates, _dTokenStates, dTokenId, to); } // @dev bPrice * bAmount / UONE = b0Amount, b0Amount in decimalsB0 function _getBPrice(address bToken) internal view returns (uint256 bPrice) { if (bToken == tokenB0) { bPrice = UONE; } else { uint8 decimalsB = bToken.decimals(); bPrice = oracle.getValue(_bTokenStates[bToken].getBytes32(I.B_ORACLEID)).itou().rescale(decimalsB, decimalsB0); if (bPrice == 0) { revert InvalidBPrice(); } } } function _getExParams(Data memory data) internal view { data.collateralFactor = _bTokenStates[data.bToken].getUint(I.B_COLLATERALFACTOR); data.bPrice = _getBPrice(data.bToken); } // @notice Calculate the liquidity (in 18 decimals) associated with current dTokenId function _getDTokenLiquidity(Data memory data) internal view returns (uint256 liquidity) { uint256 b0AmountInVault = IVault(data.vault).getBalance(data.dTokenId) * data.bPrice / UONE * data.collateralFactor / UONE; uint256 b0Shortage = data.b0Amount >= 0 ? 0 : (-data.b0Amount).itou(); if (b0AmountInVault >= b0Shortage) { liquidity = b0AmountInVault.add(data.b0Amount).rescale(decimalsB0, 18); } } // @notice Calculate the liquidity (in 18 decimals) associated with current dTokenId if `bAmount` in bToken is removed function _getDTokenLiquidityWithRemove(Data memory data, uint256 bAmount) internal view returns (uint256 liquidity) { if (bAmount < type(uint256).max / data.bPrice) { // make sure bAmount * bPrice won't overflow uint256 bAmountInVault = IVault(data.vault).getBalance(data.dTokenId); if (bAmount >= bAmountInVault) { if (data.b0Amount > 0) { uint256 b0Shortage = (bAmount - bAmountInVault) * data.bPrice / UONE; uint256 b0Amount = data.b0Amount.itou(); if (b0Amount > b0Shortage) { liquidity = (b0Amount - b0Shortage); } } } else { uint256 b0Excessive = (bAmountInVault - bAmount) * data.bPrice / UONE * data.collateralFactor / UONE; // discounted if (data.b0Amount >= 0) { liquidity = b0Excessive.add(data.b0Amount); } else { uint256 b0Shortage = (-data.b0Amount).itou(); if (b0Excessive > b0Shortage) { liquidity = (b0Excessive - b0Shortage); } } } if (liquidity > 0) { liquidity = liquidity.rescale(decimalsB0, 18); } } } function _getDTokenLiquidityWithRemoveB0(Data memory data, uint256 b0AmountToRemove) internal view returns (uint256 liquidity) { uint256 bAmountInVault = IVault(data.vault).getBalance(data.dTokenId); uint256 b0ValueOfBAmountInVault = bAmountInVault * data.bPrice / UONE * data.collateralFactor / UONE; // discounted uint256 b0Total; if (data.b0Amount >= 0) { b0Total = b0ValueOfBAmountInVault.add(data.b0Amount); } else { b0Total = b0ValueOfBAmountInVault - (-data.b0Amount).itou(); } if (b0Total > b0AmountToRemove) { liquidity = (b0Total - b0AmountToRemove).rescale(decimalsB0, 18); } } // @notice Deposit bToken with `bAmount` function _deposit(Data memory data, uint256 bAmount) internal { if (data.bToken == tokenB0) { uint256 reserved = bAmount * b0ReserveRatio / UONE; bAmount -= reserved; vault0.deposit(uint256(0), reserved); data.b0Amount += reserved.utoi(); } if (data.bToken == tokenETH) { IVault(data.vault).deposit{value: bAmount}(data.dTokenId, bAmount); } else { IVault(data.vault).deposit(data.dTokenId, bAmount); } } /** * @notice Transfer a specified amount of bToken, handling various cases. * @param data A Data struct containing information about the interaction. * @param bAmountOut The intended amount of tokens to transfer out. * @param isTd A flag indicating whether the transfer is for a trader (true) or not (false). * @return bAmount The amount of tokens actually transferred. */ function _transferOut(Data memory data, uint256 bAmountOut, bool isTd) internal returns (uint256 bAmount) { require(!ISwitchOracle(switchOracle).state()); uint256 minSwapB0Amount = 10 ** (decimalsB0 - 2); // min swap b0Amount of 0.01 USDC bAmount = bAmountOut; // Handle redemption of additional tokens to cover a negative B0 amount. if (bAmount < type(uint256).max / UONE && data.b0Amount < 0) { if (data.bToken == tokenB0) { // Redeem B0 tokens to cover the negative B0 amount. bAmount += (-data.b0Amount).itou(); } else { // Swap tokens to B0 to cover the negative B0 amount, with a slight excess to account for possible slippage. bAmount += (-data.b0Amount).itou() * UONE / data.bPrice * 105 / 100; } } // Redeem tokens from the vault using IVault interface. bAmount = IVault(data.vault).redeem(data.dTokenId, bAmount); // bAmount now represents the actual redeemed bToken. uint256 b0AmountIn; // Amount of B0 tokens going to reserves. uint256 b0AmountOut; // Amount of B0 tokens going to the user. uint256 iouAmount; // Amount of IOU tokens going to the trader. // Handle excessive tokens (more than bAmountOut). if (bAmount > bAmountOut) { uint256 bExcessive = bAmount - bAmountOut; uint256 b0Excessive; if (data.bToken == tokenB0) { b0Excessive = bExcessive; bAmount -= bExcessive; } else if (data.bToken == tokenETH) { (uint256 resultB0, uint256 resultBX) = swapper.swapExactETHForB0{value: bExcessive}(); b0Excessive = resultB0; bAmount -= resultBX; } else { (uint256 resultB0, uint256 resultBX) = swapper.swapExactBXForB0(data.bToken, bExcessive); b0Excessive = resultB0; bAmount -= resultBX; } b0AmountIn += b0Excessive; data.b0Amount += b0Excessive.utoi(); } // Handle filling the negative B0 balance, by swapping bToken into B0, if necessary. if (bAmount > 0 && data.b0Amount < 0) { uint256 owe = (-data.b0Amount).itou(); uint256 b0Fill; if (data.bToken == tokenB0) { if (bAmount >= owe) { b0Fill = owe; bAmount -= owe; } else { b0Fill = bAmount; bAmount = 0; } } else { // let owe equals to minSwapB0Amount if small, otherwise swap may fail if (owe < minSwapB0Amount) { owe = minSwapB0Amount; } if (data.bToken == tokenETH) { (uint256 resultB0, uint256 resultBX) = swapper.swapETHForExactB0{value: bAmount}(owe); b0Fill = resultB0; bAmount -= resultBX; } else { (uint256 resultB0, uint256 resultBX) = swapper.swapBXForExactB0(data.bToken, owe, bAmount); b0Fill = resultB0; bAmount -= resultBX; } } b0AmountIn += b0Fill; data.b0Amount += b0Fill.utoi(); } // Handle reserved portion when withdrawing all or operating token is tokenB0 if (data.b0Amount > 0) { uint256 amount; if (bAmountOut >= type(uint256).max / UONE) { // withdraw all amount = data.b0Amount.itou(); } else if (data.bToken == tokenB0 && bAmount < bAmountOut) { // shortage on tokenB0 amount = SafeMath.min(data.b0Amount.itou(), bAmountOut - bAmount); } if (amount > 0) { uint256 b0Out; if (amount > b0AmountIn) { // Redeem B0 tokens from vault0 uint256 b0Redeemed = vault0.redeem(uint256(0), amount - b0AmountIn); if (b0Redeemed < amount - b0AmountIn) { // b0 insufficent if (isTd) { iouAmount = amount - b0AmountIn - b0Redeemed; // Issue IOU for trader when B0 insufficent } else { revert InsufficientB0(); // Revert for Lp when B0 insufficent } } b0Out = b0AmountIn + b0Redeemed; b0AmountIn = 0; } else { b0Out = amount; b0AmountIn -= amount; } b0AmountOut += b0Out; data.b0Amount -= b0Out.utoi() + iouAmount.utoi(); } } // Deposit B0 tokens into the vault0, if any if (b0AmountIn > 0) { vault0.deposit(uint256(0), b0AmountIn); } // Transfer B0 tokens or swap them to the current operating token if (b0AmountOut > 0) { if (isTd) { // No swap from B0 to BX for trader if (data.bToken == tokenB0) { bAmount += b0AmountOut; } else { tokenB0.transferOut(data.account, b0AmountOut); } } else { // Swap B0 into BX for Lp if (data.bToken == tokenB0) { bAmount += b0AmountOut; } else if (b0AmountOut < minSwapB0Amount) { // cannot swap such small amount of B0, cumulate it into cumulativePnlOnGateway data.cumulativePnlOnGateway = data.cumulativePnlOnGateway.addUnchecked(b0AmountOut.utoi().rescale(decimalsB0, 18)); } else if (data.bToken == tokenETH) { (, uint256 resultBX) = swapper.swapExactB0ForETH(b0AmountOut); bAmount += resultBX; } else { (, uint256 resultBX) = swapper.swapExactB0ForBX(data.bToken, b0AmountOut); bAmount += resultBX; } } } // Transfer the remaining bAmount to the user's account. if (bAmount > 0) { data.bToken.transferOut(data.account, bAmount); } // Mint IOU tokens for the trader, if any. if (iouAmount > 0) { iou.mint(data.account, iouAmount); } } /** * @dev Update liquidity-related state variables for a specific `lTokenId`. * @param lTokenId The ID of the corresponding lToken. * @param newLiquidity The new liquidity amount for the lToken. * @param newTotalLiquidity The new total liquidity in the engine. */ function _updateLiquidity(uint256 lTokenId, uint256 newLiquidity, uint256 newTotalLiquidity) internal { (uint256 cumulativeTimePerLiquidity, uint256 cumulativeTime) = getCumulativeTime(lTokenId); _gatewayStates.set(I.S_LIQUIDITYTIME, block.timestamp); _gatewayStates.set(I.S_TOTALLIQUIDITY, newTotalLiquidity); _gatewayStates.set(I.S_CUMULATIVETIMEPERLIQUIDITY, cumulativeTimePerLiquidity); _dTokenStates[lTokenId].set(I.D_LIQUIDITY, newLiquidity); _dTokenStates[lTokenId].set(I.D_CUMULATIVETIME, cumulativeTime); _dTokenStates[lTokenId].set(I.D_LASTCUMULATIVETIMEPERLIQUIDITY, cumulativeTimePerLiquidity); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../Strings.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV // Deprecated in v4.8 } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) { // 32 is the length in bytes of hash, // enforced by the type signature above /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") mstore(0x1c, hash) message := keccak256(0x00, 0x3c) } } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, "\x19\x01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) data := keccak256(ptr, 0x42) } } /** * @dev Returns an Ethereum Signed Data with intended validator, created from a * `validator` and `data` according to the version 0 of EIP-191. * * See {recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x00", validator, data)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import '../vault/IVault.sol'; import '../token/IDToken.sol'; import '../token/IIOU.sol'; import '../../oracle/IOracle.sol'; import '../swapper/ISwapper.sol'; import './IGateway.sol'; import '../liqclaim/ILiqClaim.sol'; import '../../library/Bytes32Map.sol'; import '../../library/ETHAndERC20.sol'; import '../../library/SafeMath.sol'; import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol'; import { GatewayIndex as I } from './GatewayIndex.sol'; library GatewayHelper { using Bytes32Map for mapping(uint8 => bytes32); using ETHAndERC20 for address; using SafeMath for uint256; using SafeMath for int256; error CannotDelBToken(); error BTokenDupInitialize(); error BTokenNoSwapper(); error BTokenNoOracle(); error InvalidBToken(); error InvalidSignature(); error InsufficientExecutionFee(); event AddBToken(address bToken, address vault, bytes32 oracleId, uint256 collateralFactor); event DelBToken(address bToken); event UpdateBToken(address bToken); event SetExecutionFee(uint256 actionId, uint256 executionFee); event FinishCollectProtocolFee( uint256 amount ); uint256 constant UONE = 1e18; int256 constant ONE = 1e18; address constant tokenETH = address(1); //================================================================================ // Getters //================================================================================ function getGatewayState(mapping(uint8 => bytes32) storage gatewayStates) external view returns (IGateway.GatewayState memory s) { s.cumulativePnlOnGateway = gatewayStates.getInt(I.S_CUMULATIVEPNLONGATEWAY); s.liquidityTime = gatewayStates.getUint(I.S_LIQUIDITYTIME); s.totalLiquidity = gatewayStates.getUint(I.S_TOTALLIQUIDITY); s.cumulativeTimePerLiquidity = gatewayStates.getInt(I.S_CUMULATIVETIMEPERLIQUIDITY); s.gatewayRequestId = gatewayStates.getUint(I.S_GATEWAYREQUESTID); s.dChainExecutionFeePerRequest = gatewayStates.getUint(I.S_DCHAINEXECUTIONFEEPERREQUEST); s.totalIChainExecutionFee = gatewayStates.getUint(I.S_TOTALICHAINEXECUTIONFEE); s.cumulativeCollectedProtocolFee = gatewayStates.getUint(I.S_CUMULATIVECOLLECTEDPROTOCOLFEE); } function getBTokenState( mapping(address => mapping(uint8 => bytes32)) storage bTokenStates, address bToken ) external view returns (IGateway.BTokenState memory s) { s.vault = bTokenStates[bToken].getAddress(I.B_VAULT); s.oracleId = bTokenStates[bToken].getBytes32(I.B_ORACLEID); s.collateralFactor = bTokenStates[bToken].getUint(I.B_COLLATERALFACTOR); } function getLpState( mapping(address => mapping(uint8 => bytes32)) storage bTokenStates, mapping(uint256 => mapping(uint8 => bytes32)) storage dTokenStates, uint256 lTokenId ) external view returns (IGateway.LpState memory s) { s.requestId = dTokenStates[lTokenId].getUint(I.D_REQUESTID); s.bToken = dTokenStates[lTokenId].getAddress(I.D_BTOKEN); s.bAmount = IVault(bTokenStates[s.bToken].getAddress(I.B_VAULT)).getBalance(lTokenId); s.b0Amount = dTokenStates[lTokenId].getInt(I.D_B0AMOUNT); s.lastCumulativePnlOnEngine = dTokenStates[lTokenId].getInt(I.D_LASTCUMULATIVEPNLONENGINE); s.liquidity = dTokenStates[lTokenId].getUint(I.D_LIQUIDITY); s.cumulativeTime = dTokenStates[lTokenId].getUint(I.D_CUMULATIVETIME); s.lastCumulativeTimePerLiquidity = dTokenStates[lTokenId].getUint(I.D_LASTCUMULATIVETIMEPERLIQUIDITY); s.lastRequestIChainExecutionFee = dTokenStates[lTokenId].getUint(I.D_LASTREQUESTICHAINEXECUTIONFEE); s.cumulativeUnusedIChainExecutionFee = dTokenStates[lTokenId].getUint(I.D_CUMULATIVEUNUSEDICHAINEXECUTIONFEE); } function getTdState( mapping(address => mapping(uint8 => bytes32)) storage bTokenStates, mapping(uint256 => mapping(uint8 => bytes32)) storage dTokenStates, uint256 pTokenId ) external view returns (IGateway.TdState memory s) { s.requestId = dTokenStates[pTokenId].getUint(I.D_REQUESTID); s.bToken = dTokenStates[pTokenId].getAddress(I.D_BTOKEN); s.bAmount = IVault(bTokenStates[s.bToken].getAddress(I.B_VAULT)).getBalance(pTokenId); s.b0Amount = dTokenStates[pTokenId].getInt(I.D_B0AMOUNT); s.lastCumulativePnlOnEngine = dTokenStates[pTokenId].getInt(I.D_LASTCUMULATIVEPNLONENGINE); s.singlePosition = dTokenStates[pTokenId].getBool(I.D_SINGLEPOSITION); s.lastRequestIChainExecutionFee = dTokenStates[pTokenId].getUint(I.D_LASTREQUESTICHAINEXECUTIONFEE); s.cumulativeUnusedIChainExecutionFee = dTokenStates[pTokenId].getUint(I.D_CUMULATIVEUNUSEDICHAINEXECUTIONFEE); } function getCumulativeTime( mapping(uint8 => bytes32) storage gatewayStates, mapping(uint256 => mapping(uint8 => bytes32)) storage dTokenStates, uint256 lTokenId ) external view returns (uint256 cumulativeTimePerLiquidity, uint256 cumulativeTime) { uint256 liquidityTime = gatewayStates.getUint(I.S_LIQUIDITYTIME); uint256 totalLiquidity = gatewayStates.getUint(I.S_TOTALLIQUIDITY); cumulativeTimePerLiquidity = gatewayStates.getUint(I.S_CUMULATIVETIMEPERLIQUIDITY); uint256 liquidity = dTokenStates[lTokenId].getUint(I.D_LIQUIDITY); cumulativeTime = dTokenStates[lTokenId].getUint(I.D_CUMULATIVETIME); uint256 lastCumulativeTimePerLiquidity = dTokenStates[lTokenId].getUint(I.D_LASTCUMULATIVETIMEPERLIQUIDITY); if (totalLiquidity != 0) { uint256 diff1 = (block.timestamp - liquidityTime) * UONE * UONE / totalLiquidity; unchecked { cumulativeTimePerLiquidity += diff1; } if (liquidity != 0) { uint256 diff2; unchecked { diff2 = cumulativeTimePerLiquidity - lastCumulativeTimePerLiquidity; } cumulativeTime += diff2 * liquidity / UONE; } } } function getExecutionFees(mapping(uint256 => uint256) storage executionFees) external view returns (uint256[] memory fees) { fees = new uint256[](5); fees[0] = executionFees[I.ACTION_REQUESTADDLIQUIDITY]; fees[1] = executionFees[I.ACTION_REQUESTREMOVELIQUIDITY]; fees[2] = executionFees[I.ACTION_REQUESTREMOVEMARGIN]; fees[3] = executionFees[I.ACTION_REQUESTTRADE]; fees[4] = executionFees[I.ACTION_REQUESTTRADEANDREMOVEMARGIN]; } //================================================================================ // Setters //================================================================================ function addBToken( mapping(address => mapping(uint8 => bytes32)) storage bTokenStates, ISwapper swapper, IOracle oracle, IVault vault0, address tokenB0, address bToken, address vault, bytes32 oracleId, uint256 collateralFactor ) external { if (bTokenStates[bToken].getAddress(I.B_VAULT) != address(0)) { revert BTokenDupInitialize(); } if (IVault(vault).asset() != bToken) { revert InvalidBToken(); } if (bToken != tokenETH) { if (!swapper.isSupportedToken(bToken)) { revert BTokenNoSwapper(); } // Approve for swapper and vault bToken.approveMax(address(swapper)); bToken.approveMax(vault); if (bToken == tokenB0) { // The reserved portion for B0 will be deposited to vault0 bToken.approveMax(address(vault0)); } } // Check bToken oracle except B0 if (bToken != tokenB0 && oracle.getValue(oracleId) == 0) { revert BTokenNoOracle(); } bTokenStates[bToken].set(I.B_VAULT, vault); bTokenStates[bToken].set(I.B_ORACLEID, oracleId); bTokenStates[bToken].set(I.B_COLLATERALFACTOR, collateralFactor); emit AddBToken(bToken, vault, oracleId, collateralFactor); } function delBToken( mapping(address => mapping(uint8 => bytes32)) storage bTokenStates, address bToken ) external { // bToken can only be deleted when there is no deposits if (IVault(bTokenStates[bToken].getAddress(I.B_VAULT)).stTotalAmount() != 0) { revert CannotDelBToken(); } bTokenStates[bToken].del(I.B_VAULT); bTokenStates[bToken].del(I.B_ORACLEID); bTokenStates[bToken].del(I.B_COLLATERALFACTOR); emit DelBToken(bToken); } // @dev This function can be used to change bToken collateral factor function setBTokenParameter( mapping(address => mapping(uint8 => bytes32)) storage bTokenStates, address bToken, uint8 idx, bytes32 value ) external { bTokenStates[bToken].set(idx, value); emit UpdateBToken(bToken); } // @notice Set execution fee for actionId function setExecutionFee( mapping(uint256 => uint256) storage executionFees, uint256 actionId, uint256 executionFee ) external { executionFees[actionId] = executionFee; emit SetExecutionFee(actionId, executionFee); } function setDChainExecutionFeePerRequest( mapping(uint8 => bytes32) storage gatewayStates, uint256 dChainExecutionFeePerRequest ) external { gatewayStates.set(I.S_DCHAINEXECUTIONFEEPERREQUEST, dChainExecutionFeePerRequest); } // @notic Claim dChain executionFee to account `to` function claimDChainExecutionFee( mapping(uint8 => bytes32) storage gatewayStates, address to ) external { tokenETH.transferOut(to, tokenETH.balanceOfThis() - gatewayStates.getUint(I.S_TOTALICHAINEXECUTIONFEE)); } // @notice Claim unused iChain execution fee for dTokenId function claimUnusedIChainExecutionFee( mapping(uint8 => bytes32) storage gatewayStates, mapping(uint256 => mapping(uint8 => bytes32)) storage dTokenStates, IDToken lToken, IDToken pToken, uint256 dTokenId, bool isLp ) external { address owner = isLp ? lToken.ownerOf(dTokenId) : pToken.ownerOf(dTokenId); uint256 cumulativeUnusedIChainExecutionFee = dTokenStates[dTokenId].getUint(I.D_CUMULATIVEUNUSEDICHAINEXECUTIONFEE); if (cumulativeUnusedIChainExecutionFee > 0) { uint256 totalIChainExecutionFee = gatewayStates.getUint(I.S_TOTALICHAINEXECUTIONFEE); totalIChainExecutionFee -= cumulativeUnusedIChainExecutionFee; gatewayStates.set(I.S_TOTALICHAINEXECUTIONFEE, totalIChainExecutionFee); dTokenStates[dTokenId].del(I.D_CUMULATIVEUNUSEDICHAINEXECUTIONFEE); tokenETH.transferOut(owner, cumulativeUnusedIChainExecutionFee); } } // @notice Redeem B0 for burning IOU function redeemIOU( address tokenB0, IVault vault0, IIOU iou, address to, uint256 b0Amount ) external { if (b0Amount > 0) { uint256 b0Redeemed = vault0.redeem(uint256(0), b0Amount); if (b0Redeemed > 0) { iou.burn(to, b0Redeemed); tokenB0.transferOut(to, b0Redeemed); } } } function verifyEventData( bytes memory eventData, bytes memory signature, uint256 eventDataLength, address dChainEventSigner ) external pure { require(eventData.length == eventDataLength, 'Wrong eventData length'); bytes32 hash = ECDSA.toEthSignedMessageHash(keccak256(eventData)); if (ECDSA.recover(hash, signature) != dChainEventSigner) { revert InvalidSignature(); } } //================================================================================ // Interactions //================================================================================ function finishCollectProtocolFee( mapping(uint8 => bytes32) storage gatewayStates, IVault vault0, address tokenB0, address protocolFeeManager, uint256 cumulativeCollectedProtocolFeeOnEngine ) external { uint8 decimalsB0 = tokenB0.decimals(); uint256 cumulativeCollectedProtocolFeeOnGateway = gatewayStates.getUint(I.S_CUMULATIVECOLLECTEDPROTOCOLFEE); if (cumulativeCollectedProtocolFeeOnEngine > cumulativeCollectedProtocolFeeOnGateway) { uint256 amount = (cumulativeCollectedProtocolFeeOnEngine - cumulativeCollectedProtocolFeeOnGateway).rescaleDown(18, decimalsB0); if (amount > 0) { amount = vault0.redeem(uint256(0), amount); tokenB0.transferOut(protocolFeeManager, amount); cumulativeCollectedProtocolFeeOnGateway += amount.rescale(decimalsB0, 18); gatewayStates.set(I.S_CUMULATIVECOLLECTEDPROTOCOLFEE, cumulativeCollectedProtocolFeeOnGateway); emit FinishCollectProtocolFee( amount ); } } } function liquidateRedeemAndSwap( uint8 decimalsB0, address bToken, address swapper, address liqClaim, address pToken, uint256 pTokenId, int256 b0Amount, uint256 bAmount, int256 maintenanceMarginRequired ) external returns (uint256) { uint256 b0AmountIn; // only swap needed B0 to cover maintenanceMarginRequired int256 requiredB0Amount = maintenanceMarginRequired.rescaleUp(18, decimalsB0) - b0Amount; if (requiredB0Amount > 0) { if (bToken == tokenETH) { (uint256 resultB0, uint256 resultBX) = ISwapper(swapper).swapETHForExactB0{value:bAmount}(requiredB0Amount.itou()); b0AmountIn += resultB0; bAmount -= resultBX; } else { (uint256 resultB0, uint256 resultBX) = ISwapper(swapper).swapBXForExactB0(bToken, requiredB0Amount.itou(), bAmount); b0AmountIn += resultB0; bAmount -= resultBX; } } if (bAmount > 0) { bToken.transferOut(liqClaim, bAmount); ILiqClaim(liqClaim).registerDeposit(IDToken(pToken).ownerOf(pTokenId), bToken, bAmount); } return b0AmountIn; } function receiveExecutionFee( mapping(uint8 => bytes32) storage gatewayStates, mapping(uint256 => mapping(uint8 => bytes32)) storage dTokenStates, uint256 dTokenId, uint256 executionFee ) external returns (uint256) { uint256 dChainExecutionFee = gatewayStates.getUint(I.S_DCHAINEXECUTIONFEEPERREQUEST); if (msg.value < executionFee) { revert InsufficientExecutionFee(); } uint256 iChainExecutionFee = executionFee - dChainExecutionFee; uint256 totalIChainExecutionFee = gatewayStates.getUint(I.S_TOTALICHAINEXECUTIONFEE) + iChainExecutionFee; gatewayStates.set(I.S_TOTALICHAINEXECUTIONFEE, totalIChainExecutionFee); uint256 lastRequestIChainExecutionFee = dTokenStates[dTokenId].getUint(I.D_LASTREQUESTICHAINEXECUTIONFEE); uint256 cumulativeUnusedIChainExecutionFee = dTokenStates[dTokenId].getUint(I.D_CUMULATIVEUNUSEDICHAINEXECUTIONFEE); cumulativeUnusedIChainExecutionFee += lastRequestIChainExecutionFee; lastRequestIChainExecutionFee = iChainExecutionFee; dTokenStates[dTokenId].set(I.D_LASTREQUESTICHAINEXECUTIONFEE, lastRequestIChainExecutionFee); dTokenStates[dTokenId].set(I.D_CUMULATIVEUNUSEDICHAINEXECUTIONFEE, cumulativeUnusedIChainExecutionFee); return msg.value - executionFee; } function transferLastRequestIChainExecutionFee( mapping(uint8 => bytes32) storage gatewayStates, mapping(uint256 => mapping(uint8 => bytes32)) storage dTokenStates, uint256 dTokenId, address to ) external { uint256 lastRequestIChainExecutionFee = dTokenStates[dTokenId].getUint(I.D_LASTREQUESTICHAINEXECUTIONFEE); if (lastRequestIChainExecutionFee > 0) { uint256 totalIChainExecutionFee = gatewayStates.getUint(I.S_TOTALICHAINEXECUTIONFEE); totalIChainExecutionFee -= lastRequestIChainExecutionFee; gatewayStates.set(I.S_TOTALICHAINEXECUTIONFEE, totalIChainExecutionFee); dTokenStates[dTokenId].del(I.D_LASTREQUESTICHAINEXECUTIONFEE); tokenETH.transferOut(to, lastRequestIChainExecutionFee); } } function calculateReward( int256 lpPnl, int256 minLiquidationReward, int256 maxLiquidationReward, int256 liquidationRewardCutRatio ) external pure returns (int256 reward) { if (lpPnl <= minLiquidationReward) { reward = minLiquidationReward; } else { reward = SafeMath.min( (lpPnl - minLiquidationReward) * liquidationRewardCutRatio / ONE + minLiquidationReward, maxLiquidationReward ); } } function processReward( address tokenB0, IVault vault0, int256 reward, uint256 b0AmountIn, address executor, address finisher ) external returns (int256, uint256) { uint256 uReward = reward.itou(); if (uReward <= b0AmountIn) { b0AmountIn -= uReward; } else { uint256 b0Redeemed = vault0.redeem(uint256(0), uReward - b0AmountIn); uReward = b0AmountIn + b0Redeemed; reward = uReward.utoi(); b0AmountIn = 0; } if (uReward > 0) { uint256 rewardExecutor = uReward * 80 / 100; // 80% uint256 rewardFinisher = uReward - rewardExecutor; // 20% tokenB0.transferOut(executor, rewardExecutor); tokenB0.transferOut(finisher, rewardFinisher); } return (reward, b0AmountIn); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; library GatewayIndex { uint8 constant S_CUMULATIVEPNLONGATEWAY = 1; // Cumulative pnl on Gateway uint8 constant S_LIQUIDITYTIME = 2; // Last timestamp when liquidity updated uint8 constant S_TOTALLIQUIDITY = 3; // Total liquidity on d-chain uint8 constant S_CUMULATIVETIMEPERLIQUIDITY = 4; // Cumulavie time per liquidity uint8 constant S_GATEWAYREQUESTID = 5; // Gateway request id uint8 constant S_DCHAINEXECUTIONFEEPERREQUEST = 6; // dChain execution fee for executing request on dChain uint8 constant S_TOTALICHAINEXECUTIONFEE = 7; // Total iChain execution fee paid by all requests uint8 constant S_CUMULATIVECOLLECTEDPROTOCOLFEE = 8; // Cumulative collected protocol fee on Gateway uint8 constant B_VAULT = 1; // BToken vault address uint8 constant B_ORACLEID = 2; // BToken oracle id uint8 constant B_COLLATERALFACTOR = 3; // BToken collateral factor uint8 constant D_REQUESTID = 1; // Lp/Trader request id uint8 constant D_BTOKEN = 2; // Lp/Trader bToken uint8 constant D_B0AMOUNT = 3; // Lp/Trader b0Amount uint8 constant D_LASTCUMULATIVEPNLONENGINE = 4; // Lp/Trader last cumulative pnl on engine uint8 constant D_LIQUIDITY = 5; // Lp liquidity uint8 constant D_CUMULATIVETIME = 6; // Lp cumulative time uint8 constant D_LASTCUMULATIVETIMEPERLIQUIDITY = 7; // Lp last cumulative time per liquidity uint8 constant D_SINGLEPOSITION = 8; // Td single position flag uint8 constant D_LASTREQUESTICHAINEXECUTIONFEE = 9; // User last request's iChain execution fee uint8 constant D_CUMULATIVEUNUSEDICHAINEXECUTIONFEE = 10; // User cumulaitve iChain execution fee for requests cannot be finished, users can claim back uint8 constant D_CURRENTOPERATETOKEN = 11; uint256 constant ACTION_REQUESTADDLIQUIDITY = 1; uint256 constant ACTION_REQUESTREMOVELIQUIDITY = 2; uint256 constant ACTION_REQUESTREMOVEMARGIN = 3; uint256 constant ACTION_REQUESTTRADE = 4; uint256 constant ACTION_REQUESTTRADEANDREMOVEMARGIN = 5; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import '../../utils/Admin.sol'; import '../../utils/Implementation.sol'; import '../../utils/ReentryLock.sol'; abstract contract GatewayStorage is Admin, Implementation, ReentryLock { // stateId => value mapping(uint8 => bytes32) internal _gatewayStates; // bToken => stateId => value mapping(address => mapping(uint8 => bytes32)) internal _bTokenStates; // dTokenId => stateId => value mapping(uint256 => mapping(uint8 => bytes32)) internal _dTokenStates; // actionId => executionFee mapping(uint256 => uint256) internal _executionFees; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; interface IGateway { struct GatewayParam { address lToken; address pToken; address oracle; address swapper; address vault0; address iou; address tokenB0; address dChainEventSigner; uint256 b0ReserveRatio; int256 liquidationRewardCutRatio; int256 minLiquidationReward; int256 maxLiquidationReward; address protocolFeeManager; address liqClaim; address switchOracle; } struct GatewayState { int256 cumulativePnlOnGateway; uint256 liquidityTime; uint256 totalLiquidity; int256 cumulativeTimePerLiquidity; uint256 gatewayRequestId; uint256 dChainExecutionFeePerRequest; uint256 totalIChainExecutionFee; uint256 cumulativeCollectedProtocolFee; } struct BTokenState { address vault; bytes32 oracleId; uint256 collateralFactor; } struct LpState { uint256 requestId; address bToken; uint256 bAmount; int256 b0Amount; int256 lastCumulativePnlOnEngine; uint256 liquidity; uint256 cumulativeTime; uint256 lastCumulativeTimePerLiquidity; uint256 lastRequestIChainExecutionFee; uint256 cumulativeUnusedIChainExecutionFee; } struct TdState { uint256 requestId; address bToken; uint256 bAmount; int256 b0Amount; int256 lastCumulativePnlOnEngine; bool singlePosition; uint256 lastRequestIChainExecutionFee; uint256 cumulativeUnusedIChainExecutionFee; } struct VarOnExecuteUpdateLiquidity { uint256 requestId; uint256 lTokenId; uint256 liquidity; uint256 totalLiquidity; int256 cumulativePnlOnEngine; uint256 bAmountToRemove; } struct VarOnExecuteRemoveMargin { uint256 requestId; uint256 pTokenId; uint256 requiredMargin; int256 cumulativePnlOnEngine; uint256 bAmountToRemove; } struct VarOnExecuteLiquidate { address requester; address executor; address finisher; uint256 requestId; uint256 pTokenId; int256 cumulativePnlOnEngine; int256 maintenanceMarginRequired; } struct VarOnExecuteCollectProtocolFee { uint256 chainId; uint256 cumulativeCollectedProtocolFeeOnEngine; } function getGatewayParam() external view returns (GatewayParam memory p); function getGatewayState() external view returns (GatewayState memory s); function getBTokenState(address bToken) external view returns (BTokenState memory s); function getLpState(uint256 lTokenId) external view returns (LpState memory s); function getTdState(uint256 pTokenId) external view returns (TdState memory s); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; interface ILiqClaim { struct Claimable { address bToken; uint256 amount; } function getClaimables(address owner) external view returns (Claimable[] memory); function getTotalAmount(address bToken) external view returns (uint256); function registerDeposit(address owner, address bToken, uint256 amount) external; function redeem() external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; interface ISwapper { function isSupportedToken(address tokenBX) external view returns (bool); function swapExactB0ForBX(address tokenBX, uint256 amountB0) external returns (uint256 resultB0, uint256 resultBX); function swapExactBXForB0(address tokenBX, uint256 amountBX) external returns (uint256 resultB0, uint256 resultBX); function swapB0ForExactBX(address tokenBX, uint256 maxAmountB0, uint256 amountBX) external returns (uint256 resultB0, uint256 resultBX); function swapBXForExactB0(address tokenBX, uint256 amountB0, uint256 maxAmountBX) external returns (uint256 resultB0, uint256 resultBX); function swapExactB0ForETH(uint256 amountB0) external returns (uint256 resultB0, uint256 resultBX); function swapExactETHForB0() external payable returns (uint256 resultB0, uint256 resultBX); function swapB0ForExactETH(uint256 maxAmountB0, uint256 amountBX) external returns (uint256 resultB0, uint256 resultBX); function swapETHForExactB0(uint256 amountB0) external payable returns (uint256 resultB0, uint256 resultBX); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import '@openzeppelin/contracts/token/ERC721/IERC721.sol'; interface IDToken is IERC721 { function ownerOf(uint256) external view returns (address); function totalMinted() external view returns (uint160); function mint(address owner) external returns (uint256 tokenId); function burn(uint256 tokenId) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; interface IIOU is IERC20 { function vault() external view returns (address); function mint(address account, uint256 amount) external; function burn(address account, uint256 amount) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; interface IVault { function stAmounts(uint256 dTokenId) external view returns (uint256); function stTotalAmount() external view returns (uint256); function requester() external view returns (address); function asset() external view returns (address); function getBalance(uint256 dTokenId) external view returns (uint256 balance); function deposit(uint256 dTokenId, uint256 amount) external payable returns (uint256 mintedSt); function redeem(uint256 dTokenId, uint256 amount) external returns (uint256 redeemedAmount); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; library Bytes32 { error StringExceeds31Bytes(string value); function toUint(bytes32 value) internal pure returns (uint256) { return uint256(value); } function toInt(bytes32 value) internal pure returns (int256) { return int256(uint256(value)); } function toAddress(bytes32 value) internal pure returns (address) { return address(uint160(uint256(value))); } function toBool(bytes32 value) internal pure returns (bool) { return value != bytes32(0); } /** * @notice Convert a bytes32 value to a string. * @dev This function takes an input bytes32 'value' and converts it into a string. * It dynamically determines the length of the string based on non-null characters in 'value'. * @param value The input bytes32 value to be converted. * @return The string representation of the input bytes32. */ function toString(bytes32 value) internal pure returns (string memory) { bytes memory bytesArray = new bytes(32); for (uint256 i = 0; i < 32; i++) { if (value[i] == 0) { assembly { mstore(bytesArray, i) } break; } else { bytesArray[i] = value[i]; } } return string(bytesArray); } function toBytes32(uint256 value) internal pure returns (bytes32) { return bytes32(value); } function toBytes32(int256 value) internal pure returns (bytes32) { return bytes32(uint256(value)); } function toBytes32(address value) internal pure returns (bytes32) { return bytes32(uint256(uint160(value))); } function toBytes32(bool value) internal pure returns (bytes32) { return bytes32(uint256(value ? 1 : 0)); } /** * @notice Convert a string to a bytes32 value. * @dev This function takes an input string 'value' and converts it into a bytes32 value. * It enforces a length constraint of 31 characters or less to ensure it fits within a bytes32. * The function uses inline assembly to efficiently copy the string data into the bytes32. * @param value The input string to be converted. * @return The bytes32 representation of the input string. */ function toBytes32(string memory value) internal pure returns (bytes32) { if (bytes(value).length > 31) { revert StringExceeds31Bytes(value); } bytes32 res; assembly { res := mload(add(value, 0x20)) } return res; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import './Bytes32.sol'; library Bytes32Map { function getBytes32(mapping(uint8 => bytes32) storage store, uint8 idx) internal view returns (bytes32) { return store[idx]; } function getAddress(mapping(uint8 => bytes32) storage store, uint8 idx) internal view returns (address) { return Bytes32.toAddress(store[idx]); } function getUint(mapping(uint8 => bytes32) storage store, uint8 idx) internal view returns (uint256) { return Bytes32.toUint(store[idx]); } function getInt(mapping(uint8 => bytes32) storage store, uint8 idx) internal view returns (int256) { return Bytes32.toInt(store[idx]); } function getBool(mapping(uint8 => bytes32) storage store, uint8 idx) internal view returns (bool) { return Bytes32.toBool(store[idx]); } function getString(mapping(uint8 => bytes32) storage store, uint8 idx) internal view returns (string memory) { return Bytes32.toString(store[idx]); } function set(mapping(uint8 => bytes32) storage store, uint8 idx, bytes32 value) internal { store[idx] = value; } function set(mapping(uint8 => bytes32) storage store, uint8 idx, address value) internal { store[idx] = Bytes32.toBytes32(value); } function set(mapping(uint8 => bytes32) storage store, uint8 idx, uint256 value) internal { store[idx] = Bytes32.toBytes32(value); } function set(mapping(uint8 => bytes32) storage store, uint8 idx, int256 value) internal { store[idx] = Bytes32.toBytes32(value); } function set(mapping(uint8 => bytes32) storage store, uint8 idx, bool value) internal { store[idx] = Bytes32.toBytes32(value); } function set(mapping(uint8 => bytes32) storage store, uint8 idx, string memory value) internal { store[idx] = Bytes32.toBytes32(value); } function del(mapping(uint8 => bytes32) storage store, uint8 idx) internal { delete store[idx]; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol'; import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; /// Library for operating ERC20 and ETH in one logic /// ETH is represented by address: 0x0000000000000000000000000000000000000001 library ETHAndERC20 { using SafeERC20 for IERC20; error SendEthFail(); error WrongTokenInAmount(); error WrongTokenOutAmount(); function decimals(address token) internal view returns (uint8) { return token == address(1) ? 18 : IERC20Metadata(token).decimals(); } // @notice Get the balance of ERC20 tokens or Ether held by this contract function balanceOfThis(address token) internal view returns (uint256) { return token == address(1) ? address(this).balance : IERC20(token).balanceOf(address(this)); } function approveMax(address token, address spender) internal { if (token != address(1)) { uint256 allowance = IERC20(token).allowance(address(this), spender); if (allowance != type(uint256).max) { if (allowance != 0) { IERC20(token).safeApprove(spender, 0); } IERC20(token).safeApprove(spender, type(uint256).max); } } } function unapprove(address token, address spender) internal { if (token != address(1)) { uint256 allowance = IERC20(token).allowance(address(this), spender); if (allowance != 0) { IERC20(token).safeApprove(spender, 0); } } } // @notice Transfer ERC20 tokens or Ether from 'from' to this contract function transferIn(address token, address from, uint256 amount) internal { if (token == address(1)) { if (amount != msg.value) { revert WrongTokenInAmount(); } } else { uint256 balance1 = balanceOfThis(token); IERC20(token).safeTransferFrom(from, address(this), amount); uint256 balance2 = balanceOfThis(token); if (balance2 != balance1 + amount) { revert WrongTokenInAmount(); } } } // @notice Transfer ERC20 tokens or Ether from this contract to 'to' function transferOut(address token, address to, uint256 amount) internal { uint256 balance1 = balanceOfThis(token); if (token == address(1)) { (bool success, ) = payable(to).call{value: amount}(''); if (!success) { revert SendEthFail(); } } else { IERC20(token).safeTransfer(to, amount); } uint256 balance2 = balanceOfThis(token); if (balance1 != balance2 + amount) { revert WrongTokenOutAmount(); } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; library SafeMath { error UtoIOverflow(uint256); error IToUOverflow(int256); error AbsOverflow(int256); uint256 constant IMAX = 2**255 - 1; int256 constant IMIN = -2**255; function utoi(uint256 a) internal pure returns (int256) { if (a > IMAX) { revert UtoIOverflow(a); } return int256(a); } function itou(int256 a) internal pure returns (uint256) { if (a < 0) { revert IToUOverflow(a); } return uint256(a); } function abs(int256 a) internal pure returns (int256) { if (a == IMIN) { revert AbsOverflow(a); } return a >= 0 ? a : -a; } function add(uint256 a, int256 b) internal pure returns (uint256) { if (b >= 0) { return a + uint256(b); } else { return a - uint256(-b); } } function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } function max(int256 a, int256 b) internal pure returns (int256) { return a >= b ? a : b; } function min(uint256 a, uint256 b) internal pure returns (uint256) { return a <= b ? a : b; } function min(int256 a, int256 b) internal pure returns (int256) { return a <= b ? a : b; } function divRoundingUp(uint256 a, uint256 b) internal pure returns (uint256 c) { c = a / b; if (b * c != a) { c += 1; } } // @notice Rescale a uint256 value from a base of 10^decimals1 to 10^decimals2 function rescale(uint256 value, uint256 decimals1, uint256 decimals2) internal pure returns (uint256) { return decimals1 == decimals2 ? value : value * 10**decimals2 / 10**decimals1; } // @notice Rescale value with rounding down function rescaleDown(uint256 value, uint256 decimals1, uint256 decimals2) internal pure returns (uint256) { return rescale(value, decimals1, decimals2); } // @notice Rescale value with rounding up function rescaleUp(uint256 value, uint256 decimals1, uint256 decimals2) internal pure returns (uint256) { uint256 rescaled = rescale(value, decimals1, decimals2); if (rescale(rescaled, decimals2, decimals1) != value) { rescaled += 1; } return rescaled; } function rescale(int256 value, uint256 decimals1, uint256 decimals2) internal pure returns (int256) { return decimals1 == decimals2 ? value : value * int256(10**decimals2) / int256(10**decimals1); } function rescaleDown(int256 value, uint256 decimals1, uint256 decimals2) internal pure returns (int256) { int256 rescaled = rescale(value, decimals1, decimals2); if (value < 0 && rescale(rescaled, decimals2, decimals1) != value) { rescaled -= 1; } return rescaled; } function rescaleUp(int256 value, uint256 decimals1, uint256 decimals2) internal pure returns (int256) { int256 rescaled = rescale(value, decimals1, decimals2); if (value > 0 && rescale(rescaled, decimals2, decimals1) != value) { rescaled += 1; } return rescaled; } // @notice Calculate a + b with overflow allowed function addUnchecked(int256 a, int256 b) internal pure returns (int256 c) { unchecked { c = a + b; } } // @notice Calculate a - b with overflow allowed function minusUnchecked(int256 a, int256 b) internal pure returns (int256 c) { unchecked { c = a - b; } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import '../utils/IAdmin.sol'; import '../utils/IImplementation.sol'; interface IOracle is IAdmin, IImplementation { struct Signature { bytes32 oracleId; uint256 timestamp; int256 value; uint8 v; bytes32 r; bytes32 s; } function getValue(bytes32 oracleId) external view returns (int256); function getValueCurrentBlock(bytes32 oracleId) external returns (int256); function updateOffchainValue(Signature memory s) external; function updateOffchainValues(Signature[] memory ss) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; abstract contract Admin { error OnlyAdmin(); event NewAdmin(address newAdmin); address public admin; modifier _onlyAdmin_() { if (msg.sender != admin) { revert OnlyAdmin(); } _; } constructor () { admin = msg.sender; emit NewAdmin(admin); } /** * @notice Set a new admin for the contract. * @dev This function allows the current admin to assign a new admin address without performing any explicit verification. * It's the current admin's responsibility to ensure that the 'newAdmin' address is correct and secure. * @param newAdmin The address of the new admin. */ function setAdmin(address newAdmin) external _onlyAdmin_ { admin = newAdmin; emit NewAdmin(newAdmin); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; interface IAdmin { function admin() external view returns (address); function setAdmin(address newAdmin) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; interface IImplementation { function setImplementation(address newImplementation) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import './Admin.sol'; abstract contract Implementation is Admin { event NewImplementation(address newImplementation); address public implementation; // @notice Set a new implementation address for the contract function setImplementation(address newImplementation) external _onlyAdmin_ { implementation = newImplementation; emit NewImplementation(newImplementation); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; interface ISwitchOracle { function operator() external view returns (address); function state() external view returns (bool); function setOperator(address operator_) external; function resetState() external; function setState() external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; abstract contract ReentryLock { error Reentry(); bool internal _mutex; // @notice Lock for preventing reentrancy attacks modifier _reentryLock_() { if (_mutex) { revert Reentry(); } _mutex = true; _; _mutex = false; } }
{ "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": { "contracts/ichain/gateway/GatewayHelper.sol": { "GatewayHelper": "0xcbca586bf9706706398164bb5eb8e48f220fe408" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"components":[{"internalType":"address","name":"lToken","type":"address"},{"internalType":"address","name":"pToken","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"swapper","type":"address"},{"internalType":"address","name":"vault0","type":"address"},{"internalType":"address","name":"iou","type":"address"},{"internalType":"address","name":"tokenB0","type":"address"},{"internalType":"address","name":"dChainEventSigner","type":"address"},{"internalType":"uint256","name":"b0ReserveRatio","type":"uint256"},{"internalType":"int256","name":"liquidationRewardCutRatio","type":"int256"},{"internalType":"int256","name":"minLiquidationReward","type":"int256"},{"internalType":"int256","name":"maxLiquidationReward","type":"int256"},{"internalType":"address","name":"protocolFeeManager","type":"address"},{"internalType":"address","name":"liqClaim","type":"address"},{"internalType":"address","name":"switchOracle","type":"address"}],"internalType":"struct IGateway.GatewayParam","name":"p","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"int256","name":"","type":"int256"}],"name":"IToUOverflow","type":"error"},{"inputs":[],"name":"InsufficientB0","type":"error"},{"inputs":[],"name":"InsufficientMargin","type":"error"},{"inputs":[],"name":"InvalidBAmount","type":"error"},{"inputs":[],"name":"InvalidBPrice","type":"error"},{"inputs":[],"name":"InvalidBToken","type":"error"},{"inputs":[],"name":"InvalidLTokenId","type":"error"},{"inputs":[],"name":"InvalidPTokenId","type":"error"},{"inputs":[],"name":"InvalidRequestId","type":"error"},{"inputs":[],"name":"OnlyAdmin","type":"error"},{"inputs":[],"name":"Reentry","type":"error"},{"inputs":[],"name":"SendEthFail","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"UtoIOverflow","type":"error"},{"inputs":[],"name":"WrongTokenInAmount","type":"error"},{"inputs":[],"name":"WrongTokenOutAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalLiquidity","type":"uint256"}],"name":"FinishAddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pTokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"bToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"bAmount","type":"uint256"}],"name":"FinishAddMargin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pTokenId","type":"uint256"},{"indexed":false,"internalType":"int256","name":"lpPnl","type":"int256"}],"name":"FinishLiquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalLiquidity","type":"uint256"},{"indexed":false,"internalType":"address","name":"bToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"bAmount","type":"uint256"}],"name":"FinishRemoveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pTokenId","type":"uint256"},{"indexed":false,"internalType":"address","name":"bToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"bAmount","type":"uint256"}],"name":"FinishRemoveMargin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newImplementation","type":"address"}],"name":"NewImplementation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"realMoneyMargin","type":"uint256"},{"indexed":false,"internalType":"int256","name":"lastCumulativePnlOnEngine","type":"int256"},{"indexed":false,"internalType":"int256","name":"cumulativePnlOnGateway","type":"int256"}],"name":"RequestLiquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"realMoneyMargin","type":"uint256"},{"indexed":false,"internalType":"int256","name":"lastCumulativePnlOnEngine","type":"int256"},{"indexed":false,"internalType":"int256","name":"cumulativePnlOnGateway","type":"int256"},{"indexed":false,"internalType":"uint256","name":"bAmount","type":"uint256"}],"name":"RequestRemoveMargin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"realMoneyMargin","type":"uint256"},{"indexed":false,"internalType":"int256","name":"lastCumulativePnlOnEngine","type":"int256"},{"indexed":false,"internalType":"int256","name":"cumulativePnlOnGateway","type":"int256"},{"indexed":false,"internalType":"bytes32","name":"symbolId","type":"bytes32"},{"indexed":false,"internalType":"int256[]","name":"tradeParams","type":"int256[]"}],"name":"RequestTrade","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"realMoneyMargin","type":"uint256"},{"indexed":false,"internalType":"int256","name":"lastCumulativePnlOnEngine","type":"int256"},{"indexed":false,"internalType":"int256","name":"cumulativePnlOnGateway","type":"int256"},{"indexed":false,"internalType":"uint256","name":"bAmount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"symbolId","type":"bytes32"},{"indexed":false,"internalType":"int256[]","name":"tradeParams","type":"int256[]"}],"name":"RequestTradeAndRemoveMargin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"liquidity","type":"uint256"},{"indexed":false,"internalType":"int256","name":"lastCumulativePnlOnEngine","type":"int256"},{"indexed":false,"internalType":"int256","name":"cumulativePnlOnGateway","type":"int256"},{"indexed":false,"internalType":"uint256","name":"removeBAmount","type":"uint256"}],"name":"RequestUpdateLiquidity","type":"event"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"claimDChainExecutionFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"dTokenId","type":"uint256"},{"internalType":"bool","name":"isLp","type":"bool"}],"name":"claimUnusedIChainExecutionFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"eventData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"finishCollectProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"eventData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"finishLiquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"eventData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"finishRemoveMargin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"eventData","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"finishUpdateLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bToken","type":"address"}],"name":"getBTokenState","outputs":[{"components":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"bytes32","name":"oracleId","type":"bytes32"},{"internalType":"uint256","name":"collateralFactor","type":"uint256"}],"internalType":"struct IGateway.BTokenState","name":"s","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"lTokenId","type":"uint256"}],"name":"getCumulativeTime","outputs":[{"internalType":"uint256","name":"cumulativeTimePerLiquidity","type":"uint256"},{"internalType":"uint256","name":"cumulativeTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExecutionFees","outputs":[{"internalType":"uint256[]","name":"fees","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGatewayParam","outputs":[{"components":[{"internalType":"address","name":"lToken","type":"address"},{"internalType":"address","name":"pToken","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"swapper","type":"address"},{"internalType":"address","name":"vault0","type":"address"},{"internalType":"address","name":"iou","type":"address"},{"internalType":"address","name":"tokenB0","type":"address"},{"internalType":"address","name":"dChainEventSigner","type":"address"},{"internalType":"uint256","name":"b0ReserveRatio","type":"uint256"},{"internalType":"int256","name":"liquidationRewardCutRatio","type":"int256"},{"internalType":"int256","name":"minLiquidationReward","type":"int256"},{"internalType":"int256","name":"maxLiquidationReward","type":"int256"},{"internalType":"address","name":"protocolFeeManager","type":"address"},{"internalType":"address","name":"liqClaim","type":"address"},{"internalType":"address","name":"switchOracle","type":"address"}],"internalType":"struct IGateway.GatewayParam","name":"p","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGatewayState","outputs":[{"components":[{"internalType":"int256","name":"cumulativePnlOnGateway","type":"int256"},{"internalType":"uint256","name":"liquidityTime","type":"uint256"},{"internalType":"uint256","name":"totalLiquidity","type":"uint256"},{"internalType":"int256","name":"cumulativeTimePerLiquidity","type":"int256"},{"internalType":"uint256","name":"gatewayRequestId","type":"uint256"},{"internalType":"uint256","name":"dChainExecutionFeePerRequest","type":"uint256"},{"internalType":"uint256","name":"totalIChainExecutionFee","type":"uint256"},{"internalType":"uint256","name":"cumulativeCollectedProtocolFee","type":"uint256"}],"internalType":"struct IGateway.GatewayState","name":"s","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"lTokenId","type":"uint256"}],"name":"getLpState","outputs":[{"components":[{"internalType":"uint256","name":"requestId","type":"uint256"},{"internalType":"address","name":"bToken","type":"address"},{"internalType":"uint256","name":"bAmount","type":"uint256"},{"internalType":"int256","name":"b0Amount","type":"int256"},{"internalType":"int256","name":"lastCumulativePnlOnEngine","type":"int256"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"cumulativeTime","type":"uint256"},{"internalType":"uint256","name":"lastCumulativeTimePerLiquidity","type":"uint256"},{"internalType":"uint256","name":"lastRequestIChainExecutionFee","type":"uint256"},{"internalType":"uint256","name":"cumulativeUnusedIChainExecutionFee","type":"uint256"}],"internalType":"struct IGateway.LpState","name":"s","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pTokenId","type":"uint256"}],"name":"getTdState","outputs":[{"components":[{"internalType":"uint256","name":"requestId","type":"uint256"},{"internalType":"address","name":"bToken","type":"address"},{"internalType":"uint256","name":"bAmount","type":"uint256"},{"internalType":"int256","name":"b0Amount","type":"int256"},{"internalType":"int256","name":"lastCumulativePnlOnEngine","type":"int256"},{"internalType":"bool","name":"singlePosition","type":"bool"},{"internalType":"uint256","name":"lastRequestIChainExecutionFee","type":"uint256"},{"internalType":"uint256","name":"cumulativeUnusedIChainExecutionFee","type":"uint256"}],"internalType":"struct IGateway.TdState","name":"s","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"b0Amount","type":"uint256"}],"name":"redeemIOU","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lTokenId","type":"uint256"},{"internalType":"address","name":"bToken","type":"address"},{"internalType":"uint256","name":"bAmount","type":"uint256"}],"name":"requestAddLiquidity","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pTokenId","type":"uint256"},{"internalType":"address","name":"bToken","type":"address"},{"internalType":"uint256","name":"bAmount","type":"uint256"},{"internalType":"bool","name":"singlePosition","type":"bool"}],"name":"requestAddMargin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pTokenId","type":"uint256"},{"internalType":"address","name":"bToken","type":"address"},{"internalType":"uint256","name":"bAmount","type":"uint256"},{"internalType":"bytes32","name":"symbolId","type":"bytes32"},{"internalType":"int256[]","name":"tradeParams","type":"int256[]"},{"internalType":"bool","name":"singlePosition","type":"bool"}],"name":"requestAddMarginAndTrade","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pTokenId","type":"uint256"},{"internalType":"uint256","name":"b0Amount","type":"uint256"}],"name":"requestAddMarginB0","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pTokenId","type":"uint256"}],"name":"requestLiquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lTokenId","type":"uint256"},{"internalType":"address","name":"bToken","type":"address"},{"internalType":"uint256","name":"bAmount","type":"uint256"}],"name":"requestRemoveLiquidity","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pTokenId","type":"uint256"},{"internalType":"address","name":"bToken","type":"address"},{"internalType":"uint256","name":"bAmount","type":"uint256"}],"name":"requestRemoveMargin","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pTokenId","type":"uint256"},{"internalType":"bytes32","name":"symbolId","type":"bytes32"},{"internalType":"int256[]","name":"tradeParams","type":"int256[]"}],"name":"requestTrade","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pTokenId","type":"uint256"},{"internalType":"address","name":"bToken","type":"address"},{"internalType":"uint256","name":"bAmount","type":"uint256"},{"internalType":"bytes32","name":"symbolId","type":"bytes32"},{"internalType":"int256[]","name":"tradeParams","type":"int256[]"}],"name":"requestTradeAndRemoveMargin","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"setImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6102806040523480156200001257600080fd5b506040516200658e3803806200658e833981016040819052620000359162000223565b600080546001600160a01b031916339081179091556040519081527f71614071b88dee5e0b2ae578a9dd7b2ebbe9ae832ba419dc0242cd065a290b6c9060200160405180910390a180516001600160a01b0390811660809081526020830151821660a09081526040840151831660c09081526060850151841660e052918401518316610100528301518216610120528201805182166101405251620000db911662000146565b60ff1661018090815260e08201516001600160a01b039081166101609081526101008401516101a09081526101208501516101c09081526101408601516101e0529185015161020052928401518216610220529183015181166102405291015116610260526200036e565b60006001600160a01b038216600114620001c557816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000199573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001bf919062000342565b620001c8565b60125b92915050565b6040516101e081016001600160401b03811182821017156200020057634e487b7160e01b600052604160045260246000fd5b60405290565b80516001600160a01b03811681146200021e57600080fd5b919050565b60006101e082840312156200023757600080fd5b62000241620001ce565b6200024c8362000206565b81526200025c6020840162000206565b60208201526200026f6040840162000206565b6040820152620002826060840162000206565b6060820152620002956080840162000206565b6080820152620002a860a0840162000206565b60a0820152620002bb60c0840162000206565b60c0820152620002ce60e0840162000206565b60e082015261010083810151908201526101208084015190820152610140808401519082015261016080840151908201526101806200030f81850162000206565b908201526101a06200032384820162000206565b908201526101c06200033784820162000206565b908201529392505050565b6000602082840312156200035557600080fd5b815160ff811681146200036757600080fd5b9392505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051615f866200060860003960006137120152600081816106320152610e8501526000818161060901526127000152600081816105e20152610fb50152600081816105bb0152610f8f0152600081816105940152610fdb01526000818161056d01526146ff015260008181610cea01528181610e240152818161120801528181611a9601528181612191015281816130540152818161325d015281816133a5015281816137a50152818161412f015261497c01526000818161054501528181610b940152818161191a0152818161208d015261260f01526000818161051d0152818161099101528181610db5015281816110860152818161178001528181611b6101528181611c8b015281816124a8015281816126d8015281816138060152818161395f01528181613b8001528181613dde01528181614050015281816140ac015281816140da015281816146b401526149170152600081816104f5015281816117d001526143340152600081816104cd015281816110ae01528181611186015281816117a801528181611bac015281816124ec015281816126b001528181613e5e01528181613fcb015261475a0152600081816104a501528181610e5d015281816139d001528181613a9d01528181613c0d01528181613cee015281816141ad015261426501526000818161047e01526149a301526000818161045601528181610c0b01528181610ead015281816113190152818161142d015281816114da01528181611dce01528181612116015261452a01526000818161043101528181611405015281816119ba015281816122c20152612d080152615f866000f3fe60806040526004361061019c5760003560e01c80638769da2d116100ec578063db4015661161008a578063f7c60d4411610064578063f7c60d441461074f578063f802a69714610762578063f851a440146107e9578063fa321c261461080957600080fd5b8063db401566146106a4578063e1faff561461071a578063e926bc9d1461072d57600080fd5b8063be801c08116100c6578063be801c08146103e7578063c323acb614610407578063cd18dde314610664578063d784d4261461068457600080fd5b80638769da2d1461037f5780639b1a2e94146103b4578063aaba0398146103d457600080fd5b80635c60da1b11610159578063702e6c0611610133578063702e6c06146102fe578063704b6c021461031e57806370a1848c1461033e5780637e9459ba1461035e57600080fd5b80635c60da1b146102795780635c8e13a6146102b1578063619cf2be146102d157600080fd5b8063068689cd146101a15780630c8aeb56146101f157806316890fc2146102065780631c58d9c11461021957806323cc0ebf146102395780633e0c7fc114610259575b600080fd5b3480156101ad57600080fd5b506101c16101bc366004615036565b61081c565b6040805182516001600160a01b031681526020808401519082015291810151908201526060015b60405180910390f35b6102046101ff366004615053565b6108be565b005b6102046102143660046150e4565b610a93565b34801561022557600080fd5b5061020461023436600461526b565b610b20565b34801561024557600080fd5b506102046102543660046152ce565b6113de565b34801561026557600080fd5b506102046102743660046152fe565b6114b5565b34801561028557600080fd5b50600154610299906001600160a01b031681565b6040516001600160a01b0390911681526020016101e8565b3480156102bd57600080fd5b506102046102cc366004615036565b6115e4565b3480156102dd57600080fd5b506102f16102ec3660046152fe565b611685565b6040516101e89190615317565b34801561030a57600080fd5b506102046103193660046152fe565b611769565b34801561032a57600080fd5b50610204610339366004615036565b611826565b34801561034a57600080fd5b5061020461035936600461526b565b6118a6565b61037161036c366004615396565b611daf565b6040519081526020016101e8565b34801561038b57600080fd5b5061039f61039a3660046152fe565b611f88565b604080519283526020830191909152016101e8565b3480156103c057600080fd5b506102046103cf36600461526b565b612019565b6102046103e2366004615053565b6122a5565b3480156103f357600080fd5b506102046104023660046153e0565b612491565b34801561041357600080fd5b50604080516101e08101825260006101c08201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f00000000000000000000000000000000000000000000000000000000000000008116828401527f0000000000000000000000000000000000000000000000000000000000000000811660608301527f0000000000000000000000000000000000000000000000000000000000000000811660808301527f0000000000000000000000000000000000000000000000000000000000000000811660a08301527f0000000000000000000000000000000000000000000000000000000000000000811660c08301527f0000000000000000000000000000000000000000000000000000000000000000811660e08301527f00000000000000000000000000000000000000000000000000000000000000006101008301527f00000000000000000000000000000000000000000000000000000000000000006101208301527f00000000000000000000000000000000000000000000000000000000000000006101408301527f00000000000000000000000000000000000000000000000000000000000000006101608301527f000000000000000000000000000000000000000000000000000000000000000081166101808301527f0000000000000000000000000000000000000000000000000000000000000000166101a082015290516101e89190615402565b34801561067057600080fd5b5061020461067f36600461526b565b6125af565b34801561069057600080fd5b5061020461069f366004615036565b612778565b3480156106b057600080fd5b506106b96127f1565b6040516101e89190600061010082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b610204610728366004615549565b6128b3565b34801561073957600080fd5b506107426129c5565b6040516101e891906155bb565b61020461075d366004615053565b612a40565b34801561076e57600080fd5b5061078261077d3660046152fe565b612b57565b6040516101e89190815181526020808301516001600160a01b03169082015260408083015190820152606080830151908201526080808301519082015260a08083015115159082015260c0808301519082015260e091820151918101919091526101000190565b3480156107f557600080fd5b50600054610299906001600160a01b031681565b6102046108173660046155ff565b612c2f565b604080516060810182526000808252602082018190528183015290516340d24f1f60e11b8152600360048201526001600160a01b038316602482015273cbca586bf9706706398164bb5eb8e48f220fe408906381a49e3e90604401606060405180830381865af4158015610894573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b8919061565c565b92915050565b6108c88333612cfc565b600260005260056020527f89832631fb3c3307a103ba2c84ab569c64d6182a18893dcd163f0f1c2090733a546108ff908490612dc0565b5080600003610921576040516394613e9960e01b815260040160405180910390fd5b60008381526004602052604081206109479033908690610942906002612e55565b612e6d565b905061095281612f16565b600061095d82612f59565b90506000846001600160a01b031683604001516001600160a01b03160361098f5761098883856130a0565b90506109eb565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316856001600160a01b0316036109d257610988838561328e565b604051634033aec960e01b815260040160405180910390fd5b6109f66064836156e9565b8111610a00575060005b6000868152600460205260409020610a1a90600b876133dd565b6000610a2587613401565b60c08086015160608088015160408051868152602081018e905290810188905291820192909252608081019190915260a081018890529192507f0be79565c9c0f7b4144002bd9abeb6bd0e8f43023abae7066992c58bdff9397e91015b60405180910390a150505050505050565b6000196001600160a01b03871601610afd57600460005260056020527f3eec716f11ba9e820c81ca75eb978ffb45831ef8b7a53e5e422c26008e1ca6d55434610adc82886156fd565b1115610afb576040516394613e9960e01b815260040160405180910390fd5b505b610b0987878784611daf565b9650610b1787858585612c2f565b50505050505050565b600154600160a01b900460ff1615610b4b576040516325dbe6e160e21b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b179055604051633db1a4d360e01b815273cbca586bf9706706398164bb5eb8e48f220fe40890633db1a4d390610bbc908590859060e0907f000000000000000000000000000000000000000000000000000000000000000090600401615760565b60006040518083038186803b158015610bd457600080fd5b505af4158015610be8573d6000803e3d6000fd5b50505050600082806020019051810190610c0291906157a7565b90506000610cbf7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636352211e84608001516040518263ffffffff1660e01b8152600401610c5b91815260200190565b602060405180830381865afa158015610c78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c9c9190615845565b60808401516000818152600460205260409020610cba906002612e55565b6134b6565b90506000610cde8260c001518460a001516134d590919063ffffffff16565b9050610d0f81601260ff7f0000000000000000000000000000000000000000000000000000000000000000166134da565b8260a001818151610d209190615862565b90525060a083015160c083015260808201516020830151604051637cbc237360e01b81526004810191909152600019602482015260009182916001600160a01b0390911690637cbc2373906044016020604051808303816000875af1158015610d8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610db19190615882565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031684604001516001600160a01b031603610e0157610dfa81836156fd565b9150610f5f565b604084810151602086015160a087015160c089015193516330706e4160e01b81527f000000000000000000000000000000000000000000000000000000000000000060ff1660048201526001600160a01b0393841660248201527f0000000000000000000000000000000000000000000000000000000000000000841660448201527f0000000000000000000000000000000000000000000000000000000000000000841660648201527f0000000000000000000000000000000000000000000000000000000000000000909316608484015260a483019190915260c482015260e4810183905261010481019190915273cbca586bf9706706398164bb5eb8e48f220fe408906330706e419061012401602060405180830381865af4158015610f2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f529190615882565b610f5c90836156fd565b91505b5060008360a00151610f7083613514565b610f7a9190615862565b60405162abd53960e21b8152600481018290527f000000000000000000000000000000000000000000000000000000000000000060248201527f000000000000000000000000000000000000000000000000000000000000000060448201527f0000000000000000000000000000000000000000000000000000000000000000606482015290915060009073cbca586bf9706706398164bb5eb8e48f220fe408906302af54e490608401602060405180830381865af4158015611041573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110659190615882565b6020870151604080890151905163101b43e760e31b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f00000000000000000000000000000000000000000000000000000000000000008116602483015260448201859052606482018890529283166084820152911660a482015290915073cbca586bf9706706398164bb5eb8e48f220fe408906380da1f389060c4016040805180830381865af415801561112f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611153919061589b565b9350905061116181836158bf565b915082156111fd57604051631c57762b60e31b815260006004820152602481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e2bbb158906044016020604051808303816000875af11580156111d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111fb9190615882565b505b61123861122f8360ff7f000000000000000000000000000000000000000000000000000000000000000016601261354a565b60608701510190565b6060860152600060a086015261124d8561358a565b6080860151600090815260046020526040812061126b906009612e55565b608088015160009081526004602052604081209192509061128d90600a612e55565b608089018051600090815260046020818152604080842060098552825280842084905593518352908152828220600a835290529081208190559091506112d560026007612e55565b90506112e182846156fd565b6112eb90826158df565b90506112fa6002600783613612565b5050506080860151604051630852cd8d60e31b815260048101919091527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906342966c6890602401600060405180830381600087803b15801561136557600080fd5b505af1158015611379573d6000803e3d6000fd5b505050606080880151608089015160408051928352602083019190915281018590527f8c3b5c5c5dddce5cc5abfa2cc66ec5dd06f76f9e9bb21debb1a85ad88fdbdb6f92500160405180910390a150506001805460ff60a01b19169055505050505050565b604051630429dd8560e41b8152600260048083019190915260248201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660448301527f00000000000000000000000000000000000000000000000000000000000000001660648201526084810183905281151560a482015273cbca586bf9706706398164bb5eb8e48f220fe4089063429dd8509060c4015b60006040518083038186803b15801561149957600080fd5b505af41580156114ad573d6000803e3d6000fd5b505050505050565b6040516331a9108f60e11b815260048101829052600090611560906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636352211e90602401602060405180830381865afa158015611521573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115459190615845565b60008481526004602052604090208490610cba906002612e55565b905061156b81612f16565b600061157682612f59565b9050600061158384613401565b60c084015160608086015160408051858152602081018a905280820188905292830193909352608082015290519192507fb299642ff40e6ba13eec0f77203c3ef9816a9f64212a40467d63db0eba259b4f919081900360a00190a150505050565b6000546001600160a01b0316331461160f57604051634755657960e01b815260040160405180910390fd5b604051631428438f60e11b8152600260048201526001600160a01b038216602482015273cbca586bf9706706398164bb5eb8e48f220fe40890632850871e906044015b60006040518083038186803b15801561166a57600080fd5b505af415801561167e573d6000803e3d6000fd5b5050505050565b6116e46040518061014001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040516393760d3d60e01b8152600360048083019190915260248201526044810183905273cbca586bf9706706398164bb5eb8e48f220fe408906393760d3d9060640161014060405180830381865af4158015611745573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b891906158f2565b6040516332f6692360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f0000000000000000000000000000000000000000000000000000000000000000811660248301527f00000000000000000000000000000000000000000000000000000000000000001660448201523360648201526084810182905273cbca586bf9706706398164bb5eb8e48f220fe408906332f669239060a401611652565b6000546001600160a01b0316331461185157604051634755657960e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b0383169081179091556040519081527f71614071b88dee5e0b2ae578a9dd7b2ebbe9ae832ba419dc0242cd065a290b6c906020015b60405180910390a150565b600154600160a01b900460ff16156118d1576040516325dbe6e160e21b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b179055604051633db1a4d360e01b815273cbca586bf9706706398164bb5eb8e48f220fe40890633db1a4d390611942908590859060c0907f000000000000000000000000000000000000000000000000000000000000000090600401615760565b60006040518083038186803b15801561195a57600080fd5b505af415801561196e573d6000803e3d6000fd5b505050506000828060200190518101906119889190615980565b905061199c81602001518260000151613618565b6119b3816020015182604001518360600151613689565b6000611a6b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636352211e84602001516040518263ffffffff1660e01b8152600401611a0a91815260200190565b602060405180830381865afa158015611a27573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a4b9190615845565b602080850151600081815260049092526040909120610cba906002612e55565b90506000611a8a8260c0015184608001516134d590919063ffffffff16565b9050611abb81601260ff7f0000000000000000000000000000000000000000000000000000000000000000166134da565b8260a001818151611acc9190615862565b905250608083015160c0830152604082015160a08401516000919015611cb2576020808601516000908152600490915260409020611b0b90600b612e55565b9050806001600160a01b031684604001516001600160a01b031603611b5f57611b3384612f16565b611b58848660400151600014611b4d578660a00151611b51565b6000195b600061370e565b9150611cb2565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614611b9d57600080fd5b60008460a001511315611cb2577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316637cbc23736000611bf58860a00151611bf08960a0015161439e565b6143c4565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af1158015611c38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c5c9190615882565b9150611c6782613514565b8460a001818151611c7891906158bf565b9052508351611cb2906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690846143db565b611cbb8461358a565b611cc98560200151336144be565b8460a00151600003611d2e5784516020808701516040808901516060808b0151835196875294860193909352908401528201527f3b963caf777561b495590bdc6ec8abd33838524d0a3aafc3f6f1b7857449d0999060800160405180910390a1611d99565b84516020808701516040808901516060808b0151835196875294860193909352908401528201526001600160a01b038216608082015260a081018390527fc72ca5dc9c24b437f43b8c112e7b4e24b4a7bce4b1f405e591eee1cc32d327c29060c00160405180910390a15b50506001805460ff60a01b191690555050505050565b600084600003611e6b576040516335313c2160e11b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636a627842906024016020604051808303816000875af1158015611e1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e439190615882565b94508115611e66576000858152600460205260409020611e669060086001614515565b611e75565b611e75853361451e565b611e7e846145de565b6000611e8b3387876134b6565b90506000196001600160a01b03861601611ec05734841115611ec0576040516394613e9960e01b815260040160405180910390fd5b83600003611ee1576040516394613e9960e01b815260040160405180910390fd5b6001600160a01b038516600114611f09578051611f09906001600160a01b0387169086614628565b611f1381856146b2565b611f1c8161358a565b6000611f2787613401565b60408051828152602081018a90526001600160a01b038916818301526060810188905290519192507f8eee26bd33caee967b96e711aa45ef94c734251b0457a0f2213fb392c01ef53a919081900360800190a186925050505b949350505050565b604051632a8bb6c960e11b81526002600482810191909152602482015260448101829052600090819073cbca586bf9706706398164bb5eb8e48f220fe408906355176d92906064016040805180830381865af4158015611fec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612010919061589b565b91509150915091565b600154600160a01b900460ff1615612044576040516325dbe6e160e21b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b179055604051633db1a4d360e01b815273cbca586bf9706706398164bb5eb8e48f220fe40890633db1a4d3906120b5908590859060a0907f000000000000000000000000000000000000000000000000000000000000000090600401615760565b60006040518083038186803b1580156120cd57600080fd5b505af41580156120e1573d6000803e3d6000fd5b505050506000828060200190518101906120fb91906159f9565b905061210f81602001518260000151613618565b60006121667f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636352211e84602001516040518263ffffffff1660e01b8152600401611a0a91815260200190565b905060006121858260c0015184606001516134d590919063ffffffff16565b90506121b681601260ff7f0000000000000000000000000000000000000000000000000000000000000000166134da565b8260a0018181516121c79190615862565b905250606083015160c08301526121dd82612f16565b60006121ef838560800151600161370e565b905083604001516121ff84612f59565b101561221e576040516341c092a960e01b815260040160405180910390fd5b6122278361358a565b6122358460200151336144be565b83516020808601516040868101518151948552928401919091526001600160a01b039091168282015260608201839052517f39f415ac8ef6b49b1ba40c4fafb2b911df1c5306c49eef15c4bc38a0128fca989181900360800190a150506001805460ff60a01b1916905550505050565b8260000361233e576040516335313c2160e11b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636a627842906024016020604051808303816000875af1158015612313573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123379190615882565b9250612348565b6123488333612cfc565b612351826145de565b600061235e3385856134b6565b6001600090815260056020527f1471eb6eb2c5e789fc3de43f8ce62938c7d1836ec861730447e2ada8fd81017b549192509061239b908690612dc0565b90506000196001600160a01b038516016123b3578092505b826000036123d4576040516394613e9960e01b815260040160405180910390fd5b6001600160a01b0384166001146123fc5781516123fc906001600160a01b0386169085614628565b61240682846146b2565b61240f82612f16565b600061241a83612f59565b90506124258361358a565b600061243087613401565b60c08086015160608088015160408051868152602081018e9052908101889052918201929092526080810191909152600060a08201529192507f0be79565c9c0f7b4144002bd9abeb6bd0e8f43023abae7066992c58bdff9397e9101610a82565b61249b823361451e565b6124cf6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163383614628565b604051631c57762b60e31b815260006004820152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e2bbb158906044016020604051808303816000875af115801561253d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125619190615882565b50600082815260046020526040812061257b906003612e55565b90506125aa600361258b84613514565b6125959084615862565b60008681526004602052604090209190613612565b505050565b6000546001600160a01b031633146125da57604051634755657960e01b815260040160405180910390fd5b60408051633db1a4d360e01b815273cbca586bf9706706398164bb5eb8e48f220fe40891633db1a4d3916126379186918691907f000000000000000000000000000000000000000000000000000000000000000090600401615760565b60006040518083038186803b15801561264f57600080fd5b505af4158015612663573d6000803e3d6000fd5b5050505060008280602001905181019061267d9190615a68565b8051909150461461268d57600080fd5b602081015160405163ac46ae5160e01b8152600260048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660248301527f0000000000000000000000000000000000000000000000000000000000000000811660448301527f0000000000000000000000000000000000000000000000000000000000000000166064820152608481019190915273cbca586bf9706706398164bb5eb8e48f220fe4089063ac46ae519060a40160006040518083038186803b15801561276457600080fd5b505af4158015610b17573d6000803e3d6000fd5b6000546001600160a01b031633146127a357604051634755657960e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f6b70829fcbe4891157f7a7496f9870927de3c8237adbe9cd39bae09b7382c4099060200161189b565b61283960405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b604051637d4b09b360e01b81526002600482015273cbca586bf9706706398164bb5eb8e48f220fe40890637d4b09b39060240161010060405180830381865af415801561288a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128ae9190615ab6565b905090565b6128bd863361451e565b600560008190526020527f458b30c2d72bfd2c6317304a4594ecbafe5f729d3111b65fdc3a33bd48e5432d546128f4908790612dc0565b5083600003612916576040516394613e9960e01b815260040160405180910390fd5b60006129233388886134b6565b905061292e81612f16565b600061293982612f59565b9050600061294783886130a0565b90506129546064836156e9565b811161295e575060005b60006129698a613401565b90507ecae5d4a40f5ab841867a076a796230392d8ce403d409f84f18377b49a602d5818b848760c0015188606001518d8d8d8d6040516129b199989796959493929190615b5c565b60405180910390a150505050505050505050565b604051630afc7a3960e31b81526005600482015260609073cbca586bf9706706398164bb5eb8e48f220fe408906357e3d1c890602401600060405180830381865af4158015612a18573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526128ae9190810190615bab565b612a4a833361451e565b600360005260056020527fa9bc9a3a348c357ba16b37005d7e6b3236198c0e939f4af8c5f19b8deeb8ebc054612a81908490612dc0565b5080600003612aa3576040516394613e9960e01b815260040160405180910390fd5b6000612ab03385856134b6565b9050612abb81612f16565b6000612ac682612f59565b90506000612ad483856130a0565b9050612ae16064836156e9565b8111612aeb575060005b6000612af687613401565b60c08086015160608088015160408051868152602081018e905290810188905291820192909252608081019190915260a081018890529192507f88ad2ade0e436c3cffc2b0d1b51fb1379e5bca5dd3aaef801b234b18aa87d2a99101610a82565b612baa6040518061010001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160001515815260200160008152602001600081525090565b60405163ee55e7b560e01b8152600360048083019190915260248201526044810183905273cbca586bf9706706398164bb5eb8e48f220fe4089063ee55e7b59060640161010060405180830381865af4158015612c0b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b89190615c50565b612c39843361451e565b600460005260056020527f3eec716f11ba9e820c81ca75eb978ffb45831ef8b7a53e5e422c26008e1ca6d554612c70908590612dc0565b506000848152600460205260408120612c929033908790610cba906002612e55565b9050612c9d81612f16565b6000612ca882612f59565b90506000612cb587613401565b90507fd8eb4847ae2a642c0c24f4336eb3ac86a4cac889da47bb554f95c4d6163d80ff8188848660c0015187606001518b8b8b604051610a82989796959493929190615cd2565b806001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636352211e846040518263ffffffff1660e01b8152600401612d5491815260200190565b602060405180830381865afa158015612d71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d959190615845565b6001600160a01b031614612dbc5760405163a03dca6f60e01b815260040160405180910390fd5b5050565b60405163659d6d9960e11b815260026004808301919091526024820152604481018390526064810182905260009073cbca586bf9706706398164bb5eb8e48f220fe4089063cb3adb3290608401602060405180830381865af4158015612e2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e4e9190615882565b9392505050565b60ff8116600090815260208390526040812054612e4e565b612e75614fba565b6001600160a01b0380851682526020820184905282166040820152612e9c60026001612e55565b60608201526001600160a01b0382166000908152600360205260409020612ec4906001612e55565b6001600160a01b031660808201526000838152600460205260409020612eeb906003612e55565b60a08201526000838152600460208190526040909120612f0a91612e55565b60c08201529392505050565b6040808201516001600160a01b0316600090815260036020819052919020612f3d91612e55565b60e08201526040810151612f5090614913565b61010090910152565b600080670de0b6b3a76400008360e00151670de0b6b3a764000085610100015186608001516001600160a01b0316631e01043988602001516040518263ffffffff1660e01b8152600401612faf91815260200190565b602060405180830381865afa158015612fcc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ff09190615882565b612ffa9190615d18565b61300491906156e9565b61300e9190615d18565b61301891906156e9565b90506000808460a0015112156130435761303e8460a0015161303990615d2f565b61439e565b613046565b60005b9050808210613099576130967f000000000000000000000000000000000000000000000000000000000000000060ff16601261308f8760a0015186614a9190919063ffffffff16565b9190614abe565b92505b5050919050565b60008261010001516000196130b591906156e9565b8210156108b85760808301516020840151604051631e01043960e01b815260048101919091526000916001600160a01b031690631e01043990602401602060405180830381865afa15801561310e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131329190615882565b90508083106131a95760008460a0015113156131a4576000670de0b6b3a7640000856101000151838661316591906158df565b61316f9190615d18565b61317991906156e9565b9050600061318a8660a0015161439e565b9050818111156131a15761319e82826158df565b93505b50505b61324f565b6000670de0b6b3a76400008560e00151670de0b6b3a764000087610100015187866131d491906158df565b6131de9190615d18565b6131e891906156e9565b6131f29190615d18565b6131fc91906156e9565b905060008560a00151126132215760a085015161321a908290614a91565b925061324d565b60006132348660a0015161303990615d2f565b90508082111561324b5761324881836158df565b93505b505b505b8115613287576132848260ff7f0000000000000000000000000000000000000000000000000000000000000000166012614abe565b91505b5092915050565b60008083608001516001600160a01b0316631e01043985602001516040518263ffffffff1660e01b81526004016132c791815260200190565b602060405180830381865afa1580156132e4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133089190615882565b90506000670de0b6b3a76400008560e00151670de0b6b3a7640000876101000151856133349190615d18565b61333e91906156e9565b6133489190615d18565b61335291906156e9565b90506000808660a00151126133785760a0860151613371908390614a91565b9050613396565b6133898660a0015161303990615d2f565b61339390836158df565b90505b848111156133d4576133d160ff7f000000000000000000000000000000000000000000000000000000000000000016601261308f88856158df565b93505b50505092915050565b6001600160a01b0381165b60ff909216600090815260209390935250604090912055565b60008061341060026005612e55565b905061341d600182615d4b565b9050613435600260056001600160801b038416613612565b600083815260046020526040812061344e906001612e55565b905061345b600182615d4b565b60008581526004602052604090209091506134819060016001600160801b038416613612565b60006134ad6001600160801b0383166fffffffffffffffffffffffffffffffff19608086901b166156fd565b95945050505050565b6134be614fba565b6134c9848484612e6d565b9050612e4e8383614af1565b900390565b6000806134e885858561354a565b905060008512801561350457508461350182858761354a565b14155b15611f80576134ad6001826158bf565b60006001600160ff1b0382111561354657604051632a26bd1360e01b8152600481018390526024015b60405180910390fd5b5090565b60008183146135825761355e83600a615e4f565b61356983600a615e4f565b6135739086615e5b565b61357d9190615e8b565b611f80565b509192915050565b606081015161359e90600290600190613612565b60408082015160208084015160009081526004909152919091206135c4916002906133dd565b60a081015160208083015160009081526004909152604090206135e991600390613612565b60c081015160208083015160009081526004918290526040902061360f92909190613612565b50565b806133e8565b600082815260046020526040902081906001600160801b0382169061363e906001612e55565b1461365c576040516302e8145360e61b815260040160405180910390fd5b6125aa600161366b8382615d4b565b600086815260046020526040902091906001600160801b0316613612565b60008061369585611f88565b90925090506136a660028042613612565b6136b36002600385613612565b6136c06002600484613612565b60008581526004602052604090206136da90600586613612565b60008581526004602052604090206136f490600683613612565b600085815260046020526040902061167e90600784613612565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c19d93fb6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561376e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137929190615eb9565b1561379c57600080fd5b60006137c960027f0000000000000000000000000000000000000000000000000000000000000000615ed6565b6137d490600a615eef565b84925090506137ed670de0b6b3a76400006000196156e9565b821080156137ff575060008560a00151125b156138bb577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031685604001516001600160a01b031603613863576138528560a0015161303990615d2f565b61385c90836156fd565b91506138bb565b6064856101000151670de0b6b3a76400006138858860a0015161303990615d2f565b61388f9190615d18565b61389991906156e9565b6138a4906069615d18565b6138ae91906156e9565b6138b890836156fd565b91505b60808501516020860151604051637cbc237360e01b81526001600160a01b0390921691637cbc2373916138fb918690600401918252602082015260400190565b6020604051808303816000875af115801561391a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061393e9190615882565b9150600080600086851115613b4e57600061395988876158df565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168a604001516001600160a01b0316036139ad5750806139a681886158df565b9650613b22565b60016001600160a01b03168a604001516001600160a01b031603613a6d576000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f29216e2856040518263ffffffff1660e01b8152600401604080518083038185885af1158015613a2d573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613a52919061589b565b9093508392509050613a64818a6158df565b98505050613b22565b60408a8101519051633e87f49360e01b81526001600160a01b0391821660048201526024810184905260009182917f000000000000000000000000000000000000000000000000000000000000000090911690633e87f4939060440160408051808303816000875af1158015613ae7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b0b919061589b565b9093508392509050613b1d818a6158df565b985050505b613b2c81866156fd565b9450613b3781613514565b8a60a001818151613b489190615862565b90525050505b600085118015613b62575060008860a00151125b15613d9f576000613b7a8960a0015161303990615d2f565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168a604001516001600160a01b031603613bde57818710613bd5575080613bce81886158df565b9650613d73565b50600095613d73565b85821015613bea578591505b60016001600160a01b03168a604001516001600160a01b031603613cb7576000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316637661f4f68a866040518363ffffffff1660e01b8152600401613c5a91815260200190565b604080518083038185885af1158015613c77573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613c9c919061589b565b9093508392509050613cae818a6158df565b98505050613d73565b60408a810151905163bf441a5960e01b81526001600160a01b039182166004820152602481018490526044810189905260009182917f00000000000000000000000000000000000000000000000000000000000000009091169063bf441a599060640160408051808303816000875af1158015613d38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d5c919061589b565b9093508392509050613d6e818a6158df565b985050505b613d7d81866156fd565b9450613d8881613514565b8a60a001818151613d999190615862565b90525050505b60008860a001511315613fa8576000613dc2670de0b6b3a76400006000196156e9565b8810613ddc57613dd58960a0015161439e565b9050613e42565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031689604001516001600160a01b0316148015613e2057508786105b15613e4257613e3f613e358a60a0015161439e565b611bf0888b6158df565b90505b8015613fa657600084821115613f595760006001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016637cbc237382613e8e89876158df565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af1158015613ed1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ef59190615882565b9050613f0186846158df565b811015613f43578815613f2a5780613f1987856158df565b613f2391906158df565b9350613f43565b6040516320b7831f60e11b815260040160405180910390fd5b613f4d81876156fd565b91506000955050613f68565b5080613f6581866158df565b94505b613f7281856156fd565b9350613f7d83613514565b613f8682613514565b613f909190615862565b8a60a001818151613fa191906158bf565b905250505b505b821561404257604051631c57762b60e31b815260006004820152602481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e2bbb158906044016020604051808303816000875af115801561401c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140409190615882565b505b81156142e35785156140d8577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031688604001516001600160a01b03160361409c5761409582866156fd565b94506142e3565b87516140d3906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690846143db565b6142e3565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031688604001516001600160a01b03160361411f5761409582866156fd565b838210156141765761416c6141637f000000000000000000000000000000000000000000000000000000000000000060ff16601261415c86613514565b919061354a565b60608a01510190565b60608901526142e3565b60016001600160a01b031688604001516001600160a01b03160361423757604051632cb4d9d960e11b8152600481018390526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690635969b3b29060240160408051808303816000875af11580156141fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614221919061589b565b915061422f905081876156fd565b9550506142e3565b604088810151905163045608af60e01b81526001600160a01b039182166004820152602481018490526000917f0000000000000000000000000000000000000000000000000000000000000000169063045608af9060440160408051808303816000875af11580156142ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142d1919061589b565b91506142df905081876156fd565b9550505b84156143065787516040890151614306916001600160a01b0390911690876143db565b80156143935787516040516340c10f1960e01b81526001600160a01b039182166004820152602481018390527f0000000000000000000000000000000000000000000000000000000000000000909116906340c10f1990604401600060405180830381600087803b15801561437a57600080fd5b505af115801561438e573d6000803e3d6000fd5b505050505b505050509392505050565b60008082121561354657604051632a33bb3160e01b81526004810183905260240161353d565b6000818311156143d45781612e4e565b5090919050565b60006143e684614bf3565b90506000196001600160a01b03851601614474576000836001600160a01b03168360405160006040518083038185875af1925050503d8060008114614447576040519150601f19603f3d011682016040523d82523d6000602084013e61444c565b606091505b505090508061446e5760405163e277d13760e01b815260040160405180910390fd5b50614488565b6144886001600160a01b0385168484614c7a565b600061449385614bf3565b905061449f83826156fd565b821461167e5760405163f4d4667360e01b815260040160405180910390fd5b60405163901b479760e01b815260026004808301919091526024820152604481018390526001600160a01b038216606482015273cbca586bf9706706398164bb5eb8e48f220fe4089063901b479790608401611481565b6133e881614cdd565b806001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636352211e846040518263ffffffff1660e01b815260040161457691815260200190565b602060405180830381865afa158015614593573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145b79190615845565b6001600160a01b031614612dbc57604051639289a15b60e01b815260040160405180910390fd5b6001600160a01b0381166000908152600360205260408120614601906001612e55565b6001600160a01b03160361360f57604051634033aec960e01b815260040160405180910390fd5b6000196001600160a01b0384160161465a573481146125aa57604051630fc9bd0f60e11b815260040160405180910390fd5b600061466584614bf3565b905061467c6001600160a01b038516843085614cf7565b600061468785614bf3565b905061469383836156fd565b811461167e57604051630fc9bd0f60e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031682604001516001600160a01b0316036147ef576000670de0b6b3a76400006147247f000000000000000000000000000000000000000000000000000000000000000084615d18565b61472e91906156e9565b905061473a81836158df565b604051631c57762b60e31b815260006004820152602481018390529092507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e2bbb158906044016020604051808303816000875af11580156147ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147cf9190615882565b506147d981613514565b8360a0018181516147ea9190615862565b905250505b60016001600160a01b031682604001516001600160a01b0316036148905781608001516001600160a01b031663e2bbb158828460200151846040518463ffffffff1660e01b815260040161484d929190918252602082015260400190565b60206040518083038185885af115801561486b573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906125aa9190615882565b60808201516020830151604051631c57762b60e31b81526001600160a01b039092169163e2bbb158916148d0918590600401918252602082015260400190565b6020604051808303816000875af11580156148ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125aa9190615882565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03160361495d5750670de0b6b3a7640000919050565b6000614971836001600160a01b0316614d2f565b9050614a678160ff167f000000000000000000000000000000000000000000000000000000000000000060ff1661308f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166369843940614a086002600360008c6001600160a01b03166001600160a01b03168152602001908152602001600020614dac90919063ffffffff16565b6040518263ffffffff1660e01b8152600401614a2691815260200190565b602060405180830381865afa158015614a43573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130399190615882565b915081600003614a8a5760405163089112d560e21b815260040160405180910390fd5b505b919050565b6000808212614aab57614aa482846156fd565b90506108b8565b614ab482615d2f565b614aa490846158df565b600081831461358257614ad283600a615e4f565b614add83600a615e4f565b614ae79086615d18565b61357d91906156e9565b6000828152600460205260408120614b0a906002612e55565b90506001600160a01b03811615801590614b365750816001600160a01b0316816001600160a01b031614155b156125aa576001600160a01b0381166000908152600360205260408120614b5e906001612e55565b6001600160a01b0316635d988967856040518263ffffffff1660e01b8152600401614b8b91815260200190565b602060405180830381865afa158015614ba8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614bcc9190615882565b90508015614bed57604051634033aec960e01b815260040160405180910390fd5b50505050565b60006001600160a01b038216600114614c73576040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015614c4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c6e9190615882565b6108b8565b4792915050565b6040516001600160a01b0383166024820152604481018290526125aa90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614dc2565b600081614ceb576000614cee565b60015b60ff1692915050565b6040516001600160a01b0380851660248301528316604482015260648101829052614bed9085906323b872dd60e01b90608401614ca6565b60006001600160a01b038216600114614da457816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015614d80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c6e9190615efe565b601292915050565b60ff166000908152602091909152604090205490565b6000614e17826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614e979092919063ffffffff16565b9050805160001480614e38575080806020019051810190614e389190615eb9565b6125aa5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161353d565b6060611f80848460008585600080866001600160a01b03168587604051614ebe9190615f21565b60006040518083038185875af1925050503d8060008114614efb576040519150601f19603f3d011682016040523d82523d6000602084013e614f00565b606091505b5091509150614f1187838387614f1c565b979650505050505050565b60608315614f8b578251600003614f84576001600160a01b0385163b614f845760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161353d565b5081611f80565b611f808383815115614fa05781518083602001fd5b8060405162461bcd60e51b815260040161353d9190615f3d565b60405180610120016040528060006001600160a01b031681526020016000815260200160006001600160a01b031681526020016000815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b038116811461360f57600080fd5b60006020828403121561504857600080fd5b8135612e4e81615021565b60008060006060848603121561506857600080fd5b83359250602084013561507a81615021565b929592945050506040919091013590565b60008083601f84011261509d57600080fd5b5081356001600160401b038111156150b457600080fd5b6020830191508360208260051b85010111156150cf57600080fd5b9250929050565b801515811461360f57600080fd5b600080600080600080600060c0888a0312156150ff57600080fd5b87359650602088013561511181615021565b9550604088013594506060880135935060808801356001600160401b0381111561513a57600080fd5b6151468a828b0161508b565b90945092505060a088013561515a816150d6565b8091505092959891949750929550565b634e487b7160e01b600052604160045260246000fd5b60405161014081016001600160401b03811182821017156151a3576151a361516a565b60405290565b60405161010081016001600160401b03811182821017156151a3576151a361516a565b604051601f8201601f191681016001600160401b03811182821017156151f4576151f461516a565b604052919050565b600082601f83011261520d57600080fd5b81356001600160401b038111156152265761522661516a565b615239601f8201601f19166020016151cc565b81815284602083860101111561524e57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806040838503121561527e57600080fd5b82356001600160401b038082111561529557600080fd5b6152a1868387016151fc565b935060208501359150808211156152b757600080fd5b506152c4858286016151fc565b9150509250929050565b600080604083850312156152e157600080fd5b8235915060208301356152f3816150d6565b809150509250929050565b60006020828403121561531057600080fd5b5035919050565b8151815260208083015161014083019161533b908401826001600160a01b03169052565b5060408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151818401525092915050565b600080600080608085870312156153ac57600080fd5b8435935060208501356153be81615021565b92506040850135915060608501356153d5816150d6565b939692955090935050565b600080604083850312156153f357600080fd5b50508035926020909101359150565b81516001600160a01b031681526101e08101602083015161542e60208401826001600160a01b03169052565b50604083015161544960408401826001600160a01b03169052565b50606083015161546460608401826001600160a01b03169052565b50608083015161547f60808401826001600160a01b03169052565b5060a083015161549a60a08401826001600160a01b03169052565b5060c08301516154b560c08401826001600160a01b03169052565b5060e08301516154d060e08401826001600160a01b03169052565b506101008381015190830152610120808401519083015261014080840151908301526101608084015190830152610180808401516001600160a01b0381168285015250506101a0838101516001600160a01b0381168483015250506101c0838101516001600160a01b038116848301525b505092915050565b60008060008060008060a0878903121561556257600080fd5b86359550602087013561557481615021565b9450604087013593506060870135925060808701356001600160401b0381111561559d57600080fd5b6155a989828a0161508b565b979a9699509497509295939492505050565b6020808252825182820181905260009190848201906040850190845b818110156155f3578351835292840192918401916001016155d7565b50909695505050505050565b6000806000806060858703121561561557600080fd5b843593506020850135925060408501356001600160401b0381111561563957600080fd5b6156458782880161508b565b95989497509550505050565b8051614a8c81615021565b60006060828403121561566e57600080fd5b604051606081018181106001600160401b03821117156156905761569061516a565b604052825161569e81615021565b8152602083810151908201526040928301519281019290925250919050565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000826156f8576156f86156bd565b500490565b808201808211156108b8576108b86156d3565b60005b8381101561572b578181015183820152602001615713565b50506000910152565b6000815180845261574c816020860160208601615710565b601f01601f19169290920160200192915050565b6080815260006157736080830187615734565b82810360208401526157858187615734565b604084019590955250506001600160a01b039190911660609091015292915050565b600060e082840312156157b957600080fd5b60405160e081018181106001600160401b03821117156157db576157db61516a565b60405282516157e981615021565b815260208301516157f981615021565b6020820152604083015161580c81615021565b80604083015250606083015160608201526080830151608082015260a083015160a082015260c083015160c08201528091505092915050565b60006020828403121561585757600080fd5b8151612e4e81615021565b8082018281126000831280158216821582161715615541576155416156d3565b60006020828403121561589457600080fd5b5051919050565b600080604083850312156158ae57600080fd5b505080516020909101519092909150565b8181036000831280158383131683831282161715613287576132876156d3565b818103818111156108b8576108b86156d3565b6000610140828403121561590557600080fd5b61590d615180565b8251815261591d60208401615651565b602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152506101208084015181830152508091505092915050565b600060c0828403121561599257600080fd5b60405160c081018181106001600160401b03821117156159b4576159b461516a565b8060405250825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a08201528091505092915050565b600060a08284031215615a0b57600080fd5b60405160a081018181106001600160401b0382111715615a2d57615a2d61516a565b806040525082518152602083015160208201526040830151604082015260608301516060820152608083015160808201528091505092915050565b600060408284031215615a7a57600080fd5b604051604081018181106001600160401b0382111715615a9c57615a9c61516a565b604052825181526020928301519281019290925250919050565b60006101008284031215615ac957600080fd5b615ad16151a9565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201528091505092915050565b8183526000602080850194508260005b85811015615b5157813587529582019590820190600101615b35565b509495945050505050565b60006101008b83528a60208401528960408401528860608401528760808401528660a08401528560c08401528060e0840152615b9b8184018587615b25565b9c9b505050505050505050505050565b60006020808385031215615bbe57600080fd5b82516001600160401b0380821115615bd557600080fd5b818501915085601f830112615be957600080fd5b815181811115615bfb57615bfb61516a565b8060051b9150615c0c8483016151cc565b8181529183018401918481019088841115615c2657600080fd5b938501935b83851015615c4457845182529385019390850190615c2b565b98975050505050505050565b60006101008284031215615c6357600080fd5b615c6b6151a9565b825181526020830151615c7d81615021565b8060208301525060408301516040820152606083015160608201526080830151608082015260a0830151615cb0816150d6565b60a082015260c0838101519082015260e0928301519281019290925250919050565b8881528760208201528660408201528560608201528460808201528360a082015260e060c08201526000615d0a60e083018486615b25565b9a9950505050505050505050565b80820281158282048414176108b8576108b86156d3565b6000600160ff1b8201615d4457615d446156d3565b5060000390565b6001600160801b03818116838216019080821115613287576132876156d3565b600181815b80851115615da6578160001904821115615d8c57615d8c6156d3565b80851615615d9957918102915b93841c9390800290615d70565b509250929050565b600082615dbd575060016108b8565b81615dca575060006108b8565b8160018114615de05760028114615dea57615e06565b60019150506108b8565b60ff841115615dfb57615dfb6156d3565b50506001821b6108b8565b5060208310610133831016604e8410600b8410161715615e29575081810a6108b8565b615e338383615d6b565b8060001904821115615e4757615e476156d3565b029392505050565b6000612e4e8383615dae565b80820260008212600160ff1b84141615615e7757615e776156d3565b81810583148215176108b8576108b86156d3565b600082615e9a57615e9a6156bd565b600160ff1b821460001984141615615eb457615eb46156d3565b500590565b600060208284031215615ecb57600080fd5b8151612e4e816150d6565b60ff82811682821603908111156108b8576108b86156d3565b6000612e4e60ff841683615dae565b600060208284031215615f1057600080fd5b815160ff81168114612e4e57600080fd5b60008251615f33818460208701615710565b9190910192915050565b602081526000612e4e602083018461573456fea2646970667358221220e2baf2c8c978e5b836b2d7d23da6f71327c5bd537b9b46c1a3da24f0c128d04b64736f6c6343000814003300000000000000000000000060138081198b75aaf15aca3a17ec7f5ffc5d460500000000000000000000000067bdd68f20a1bb06f487d29b26ad63e162a2f828000000000000000000000000bca4439e99091afb297ecb4c5672357e467664f20000000000000000000000009a34b3810d422373ba5128ffee880235003f5cac00000000000000000000000087664d190669a00ce699944c2485326d574ecd02000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f42900000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea00000000000000000000000000000000000000000000000002c68af0bb14000000000000000000000000000000000000000000000000000006f05b59d3b200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001dcd6500000000000000000000000000b5284ed1606b91e0129182d55ee7ee31c31c920c000000000000000000000000d4e08c940ddec162c2d8f3034c75c3e08f1f6032000000000000000000000000cf82aaa68b3868fd2f280f9b211edcdea9f4770d
Deployed Bytecode
0x60806040526004361061019c5760003560e01c80638769da2d116100ec578063db4015661161008a578063f7c60d4411610064578063f7c60d441461074f578063f802a69714610762578063f851a440146107e9578063fa321c261461080957600080fd5b8063db401566146106a4578063e1faff561461071a578063e926bc9d1461072d57600080fd5b8063be801c08116100c6578063be801c08146103e7578063c323acb614610407578063cd18dde314610664578063d784d4261461068457600080fd5b80638769da2d1461037f5780639b1a2e94146103b4578063aaba0398146103d457600080fd5b80635c60da1b11610159578063702e6c0611610133578063702e6c06146102fe578063704b6c021461031e57806370a1848c1461033e5780637e9459ba1461035e57600080fd5b80635c60da1b146102795780635c8e13a6146102b1578063619cf2be146102d157600080fd5b8063068689cd146101a15780630c8aeb56146101f157806316890fc2146102065780631c58d9c11461021957806323cc0ebf146102395780633e0c7fc114610259575b600080fd5b3480156101ad57600080fd5b506101c16101bc366004615036565b61081c565b6040805182516001600160a01b031681526020808401519082015291810151908201526060015b60405180910390f35b6102046101ff366004615053565b6108be565b005b6102046102143660046150e4565b610a93565b34801561022557600080fd5b5061020461023436600461526b565b610b20565b34801561024557600080fd5b506102046102543660046152ce565b6113de565b34801561026557600080fd5b506102046102743660046152fe565b6114b5565b34801561028557600080fd5b50600154610299906001600160a01b031681565b6040516001600160a01b0390911681526020016101e8565b3480156102bd57600080fd5b506102046102cc366004615036565b6115e4565b3480156102dd57600080fd5b506102f16102ec3660046152fe565b611685565b6040516101e89190615317565b34801561030a57600080fd5b506102046103193660046152fe565b611769565b34801561032a57600080fd5b50610204610339366004615036565b611826565b34801561034a57600080fd5b5061020461035936600461526b565b6118a6565b61037161036c366004615396565b611daf565b6040519081526020016101e8565b34801561038b57600080fd5b5061039f61039a3660046152fe565b611f88565b604080519283526020830191909152016101e8565b3480156103c057600080fd5b506102046103cf36600461526b565b612019565b6102046103e2366004615053565b6122a5565b3480156103f357600080fd5b506102046104023660046153e0565b612491565b34801561041357600080fd5b50604080516101e08101825260006101c08201526001600160a01b037f00000000000000000000000060138081198b75aaf15aca3a17ec7f5ffc5d4605811682527f00000000000000000000000067bdd68f20a1bb06f487d29b26ad63e162a2f828811660208301527f000000000000000000000000bca4439e99091afb297ecb4c5672357e467664f28116828401527f0000000000000000000000009a34b3810d422373ba5128ffee880235003f5cac811660608301527f00000000000000000000000087664d190669a00ce699944c2485326d574ecd02811660808301527f000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f429811660a08301527f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894811660c08301527f000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea811660e08301527f00000000000000000000000000000000000000000000000002c68af0bb1400006101008301527f00000000000000000000000000000000000000000000000006f05b59d3b200006101208301527f00000000000000000000000000000000000000000000000000000000000000006101408301527f000000000000000000000000000000000000000000000000000000001dcd65006101608301527f000000000000000000000000b5284ed1606b91e0129182d55ee7ee31c31c920c81166101808301527f000000000000000000000000d4e08c940ddec162c2d8f3034c75c3e08f1f6032166101a082015290516101e89190615402565b34801561067057600080fd5b5061020461067f36600461526b565b6125af565b34801561069057600080fd5b5061020461069f366004615036565b612778565b3480156106b057600080fd5b506106b96127f1565b6040516101e89190600061010082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b610204610728366004615549565b6128b3565b34801561073957600080fd5b506107426129c5565b6040516101e891906155bb565b61020461075d366004615053565b612a40565b34801561076e57600080fd5b5061078261077d3660046152fe565b612b57565b6040516101e89190815181526020808301516001600160a01b03169082015260408083015190820152606080830151908201526080808301519082015260a08083015115159082015260c0808301519082015260e091820151918101919091526101000190565b3480156107f557600080fd5b50600054610299906001600160a01b031681565b6102046108173660046155ff565b612c2f565b604080516060810182526000808252602082018190528183015290516340d24f1f60e11b8152600360048201526001600160a01b038316602482015273cbca586bf9706706398164bb5eb8e48f220fe408906381a49e3e90604401606060405180830381865af4158015610894573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b8919061565c565b92915050565b6108c88333612cfc565b600260005260056020527f89832631fb3c3307a103ba2c84ab569c64d6182a18893dcd163f0f1c2090733a546108ff908490612dc0565b5080600003610921576040516394613e9960e01b815260040160405180910390fd5b60008381526004602052604081206109479033908690610942906002612e55565b612e6d565b905061095281612f16565b600061095d82612f59565b90506000846001600160a01b031683604001516001600160a01b03160361098f5761098883856130a0565b90506109eb565b7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b0316856001600160a01b0316036109d257610988838561328e565b604051634033aec960e01b815260040160405180910390fd5b6109f66064836156e9565b8111610a00575060005b6000868152600460205260409020610a1a90600b876133dd565b6000610a2587613401565b60c08086015160608088015160408051868152602081018e905290810188905291820192909252608081019190915260a081018890529192507f0be79565c9c0f7b4144002bd9abeb6bd0e8f43023abae7066992c58bdff9397e91015b60405180910390a150505050505050565b6000196001600160a01b03871601610afd57600460005260056020527f3eec716f11ba9e820c81ca75eb978ffb45831ef8b7a53e5e422c26008e1ca6d55434610adc82886156fd565b1115610afb576040516394613e9960e01b815260040160405180910390fd5b505b610b0987878784611daf565b9650610b1787858585612c2f565b50505050505050565b600154600160a01b900460ff1615610b4b576040516325dbe6e160e21b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b179055604051633db1a4d360e01b815273cbca586bf9706706398164bb5eb8e48f220fe40890633db1a4d390610bbc908590859060e0907f000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea90600401615760565b60006040518083038186803b158015610bd457600080fd5b505af4158015610be8573d6000803e3d6000fd5b50505050600082806020019051810190610c0291906157a7565b90506000610cbf7f00000000000000000000000067bdd68f20a1bb06f487d29b26ad63e162a2f8286001600160a01b0316636352211e84608001516040518263ffffffff1660e01b8152600401610c5b91815260200190565b602060405180830381865afa158015610c78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c9c9190615845565b60808401516000818152600460205260409020610cba906002612e55565b6134b6565b90506000610cde8260c001518460a001516134d590919063ffffffff16565b9050610d0f81601260ff7f0000000000000000000000000000000000000000000000000000000000000006166134da565b8260a001818151610d209190615862565b90525060a083015160c083015260808201516020830151604051637cbc237360e01b81526004810191909152600019602482015260009182916001600160a01b0390911690637cbc2373906044016020604051808303816000875af1158015610d8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610db19190615882565b90507f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b031684604001516001600160a01b031603610e0157610dfa81836156fd565b9150610f5f565b604084810151602086015160a087015160c089015193516330706e4160e01b81527f000000000000000000000000000000000000000000000000000000000000000660ff1660048201526001600160a01b0393841660248201527f0000000000000000000000009a34b3810d422373ba5128ffee880235003f5cac841660448201527f000000000000000000000000d4e08c940ddec162c2d8f3034c75c3e08f1f6032841660648201527f00000000000000000000000067bdd68f20a1bb06f487d29b26ad63e162a2f828909316608484015260a483019190915260c482015260e4810183905261010481019190915273cbca586bf9706706398164bb5eb8e48f220fe408906330706e419061012401602060405180830381865af4158015610f2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f529190615882565b610f5c90836156fd565b91505b5060008360a00151610f7083613514565b610f7a9190615862565b60405162abd53960e21b8152600481018290527f000000000000000000000000000000000000000000000000000000000000000060248201527f000000000000000000000000000000000000000000000000000000001dcd650060448201527f00000000000000000000000000000000000000000000000006f05b59d3b20000606482015290915060009073cbca586bf9706706398164bb5eb8e48f220fe408906302af54e490608401602060405180830381865af4158015611041573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110659190615882565b6020870151604080890151905163101b43e760e31b81526001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894811660048301527f00000000000000000000000087664d190669a00ce699944c2485326d574ecd028116602483015260448201859052606482018890529283166084820152911660a482015290915073cbca586bf9706706398164bb5eb8e48f220fe408906380da1f389060c4016040805180830381865af415801561112f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611153919061589b565b9350905061116181836158bf565b915082156111fd57604051631c57762b60e31b815260006004820152602481018490527f00000000000000000000000087664d190669a00ce699944c2485326d574ecd026001600160a01b03169063e2bbb158906044016020604051808303816000875af11580156111d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111fb9190615882565b505b61123861122f8360ff7f000000000000000000000000000000000000000000000000000000000000000616601261354a565b60608701510190565b6060860152600060a086015261124d8561358a565b6080860151600090815260046020526040812061126b906009612e55565b608088015160009081526004602052604081209192509061128d90600a612e55565b608089018051600090815260046020818152604080842060098552825280842084905593518352908152828220600a835290529081208190559091506112d560026007612e55565b90506112e182846156fd565b6112eb90826158df565b90506112fa6002600783613612565b5050506080860151604051630852cd8d60e31b815260048101919091527f00000000000000000000000067bdd68f20a1bb06f487d29b26ad63e162a2f8286001600160a01b0316906342966c6890602401600060405180830381600087803b15801561136557600080fd5b505af1158015611379573d6000803e3d6000fd5b505050606080880151608089015160408051928352602083019190915281018590527f8c3b5c5c5dddce5cc5abfa2cc66ec5dd06f76f9e9bb21debb1a85ad88fdbdb6f92500160405180910390a150506001805460ff60a01b19169055505050505050565b604051630429dd8560e41b8152600260048083019190915260248201526001600160a01b037f00000000000000000000000060138081198b75aaf15aca3a17ec7f5ffc5d4605811660448301527f00000000000000000000000067bdd68f20a1bb06f487d29b26ad63e162a2f8281660648201526084810183905281151560a482015273cbca586bf9706706398164bb5eb8e48f220fe4089063429dd8509060c4015b60006040518083038186803b15801561149957600080fd5b505af41580156114ad573d6000803e3d6000fd5b505050505050565b6040516331a9108f60e11b815260048101829052600090611560906001600160a01b037f00000000000000000000000067bdd68f20a1bb06f487d29b26ad63e162a2f8281690636352211e90602401602060405180830381865afa158015611521573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115459190615845565b60008481526004602052604090208490610cba906002612e55565b905061156b81612f16565b600061157682612f59565b9050600061158384613401565b60c084015160608086015160408051858152602081018a905280820188905292830193909352608082015290519192507fb299642ff40e6ba13eec0f77203c3ef9816a9f64212a40467d63db0eba259b4f919081900360a00190a150505050565b6000546001600160a01b0316331461160f57604051634755657960e01b815260040160405180910390fd5b604051631428438f60e11b8152600260048201526001600160a01b038216602482015273cbca586bf9706706398164bb5eb8e48f220fe40890632850871e906044015b60006040518083038186803b15801561166a57600080fd5b505af415801561167e573d6000803e3d6000fd5b5050505050565b6116e46040518061014001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040516393760d3d60e01b8152600360048083019190915260248201526044810183905273cbca586bf9706706398164bb5eb8e48f220fe408906393760d3d9060640161014060405180830381865af4158015611745573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b891906158f2565b6040516332f6692360e01b81526001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894811660048301527f00000000000000000000000087664d190669a00ce699944c2485326d574ecd02811660248301527f000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f4291660448201523360648201526084810182905273cbca586bf9706706398164bb5eb8e48f220fe408906332f669239060a401611652565b6000546001600160a01b0316331461185157604051634755657960e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b0383169081179091556040519081527f71614071b88dee5e0b2ae578a9dd7b2ebbe9ae832ba419dc0242cd065a290b6c906020015b60405180910390a150565b600154600160a01b900460ff16156118d1576040516325dbe6e160e21b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b179055604051633db1a4d360e01b815273cbca586bf9706706398164bb5eb8e48f220fe40890633db1a4d390611942908590859060c0907f000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea90600401615760565b60006040518083038186803b15801561195a57600080fd5b505af415801561196e573d6000803e3d6000fd5b505050506000828060200190518101906119889190615980565b905061199c81602001518260000151613618565b6119b3816020015182604001518360600151613689565b6000611a6b7f00000000000000000000000060138081198b75aaf15aca3a17ec7f5ffc5d46056001600160a01b0316636352211e84602001516040518263ffffffff1660e01b8152600401611a0a91815260200190565b602060405180830381865afa158015611a27573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a4b9190615845565b602080850151600081815260049092526040909120610cba906002612e55565b90506000611a8a8260c0015184608001516134d590919063ffffffff16565b9050611abb81601260ff7f0000000000000000000000000000000000000000000000000000000000000006166134da565b8260a001818151611acc9190615862565b905250608083015160c0830152604082015160a08401516000919015611cb2576020808601516000908152600490915260409020611b0b90600b612e55565b9050806001600160a01b031684604001516001600160a01b031603611b5f57611b3384612f16565b611b58848660400151600014611b4d578660a00151611b51565b6000195b600061370e565b9150611cb2565b7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b0316816001600160a01b031614611b9d57600080fd5b60008460a001511315611cb2577f00000000000000000000000087664d190669a00ce699944c2485326d574ecd026001600160a01b0316637cbc23736000611bf58860a00151611bf08960a0015161439e565b6143c4565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af1158015611c38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c5c9190615882565b9150611c6782613514565b8460a001818151611c7891906158bf565b9052508351611cb2906001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388941690846143db565b611cbb8461358a565b611cc98560200151336144be565b8460a00151600003611d2e5784516020808701516040808901516060808b0151835196875294860193909352908401528201527f3b963caf777561b495590bdc6ec8abd33838524d0a3aafc3f6f1b7857449d0999060800160405180910390a1611d99565b84516020808701516040808901516060808b0151835196875294860193909352908401528201526001600160a01b038216608082015260a081018390527fc72ca5dc9c24b437f43b8c112e7b4e24b4a7bce4b1f405e591eee1cc32d327c29060c00160405180910390a15b50506001805460ff60a01b191690555050505050565b600084600003611e6b576040516335313c2160e11b81523360048201527f00000000000000000000000067bdd68f20a1bb06f487d29b26ad63e162a2f8286001600160a01b031690636a627842906024016020604051808303816000875af1158015611e1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e439190615882565b94508115611e66576000858152600460205260409020611e669060086001614515565b611e75565b611e75853361451e565b611e7e846145de565b6000611e8b3387876134b6565b90506000196001600160a01b03861601611ec05734841115611ec0576040516394613e9960e01b815260040160405180910390fd5b83600003611ee1576040516394613e9960e01b815260040160405180910390fd5b6001600160a01b038516600114611f09578051611f09906001600160a01b0387169086614628565b611f1381856146b2565b611f1c8161358a565b6000611f2787613401565b60408051828152602081018a90526001600160a01b038916818301526060810188905290519192507f8eee26bd33caee967b96e711aa45ef94c734251b0457a0f2213fb392c01ef53a919081900360800190a186925050505b949350505050565b604051632a8bb6c960e11b81526002600482810191909152602482015260448101829052600090819073cbca586bf9706706398164bb5eb8e48f220fe408906355176d92906064016040805180830381865af4158015611fec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612010919061589b565b91509150915091565b600154600160a01b900460ff1615612044576040516325dbe6e160e21b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b179055604051633db1a4d360e01b815273cbca586bf9706706398164bb5eb8e48f220fe40890633db1a4d3906120b5908590859060a0907f000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea90600401615760565b60006040518083038186803b1580156120cd57600080fd5b505af41580156120e1573d6000803e3d6000fd5b505050506000828060200190518101906120fb91906159f9565b905061210f81602001518260000151613618565b60006121667f00000000000000000000000067bdd68f20a1bb06f487d29b26ad63e162a2f8286001600160a01b0316636352211e84602001516040518263ffffffff1660e01b8152600401611a0a91815260200190565b905060006121858260c0015184606001516134d590919063ffffffff16565b90506121b681601260ff7f0000000000000000000000000000000000000000000000000000000000000006166134da565b8260a0018181516121c79190615862565b905250606083015160c08301526121dd82612f16565b60006121ef838560800151600161370e565b905083604001516121ff84612f59565b101561221e576040516341c092a960e01b815260040160405180910390fd5b6122278361358a565b6122358460200151336144be565b83516020808601516040868101518151948552928401919091526001600160a01b039091168282015260608201839052517f39f415ac8ef6b49b1ba40c4fafb2b911df1c5306c49eef15c4bc38a0128fca989181900360800190a150506001805460ff60a01b1916905550505050565b8260000361233e576040516335313c2160e11b81523360048201527f00000000000000000000000060138081198b75aaf15aca3a17ec7f5ffc5d46056001600160a01b031690636a627842906024016020604051808303816000875af1158015612313573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123379190615882565b9250612348565b6123488333612cfc565b612351826145de565b600061235e3385856134b6565b6001600090815260056020527f1471eb6eb2c5e789fc3de43f8ce62938c7d1836ec861730447e2ada8fd81017b549192509061239b908690612dc0565b90506000196001600160a01b038516016123b3578092505b826000036123d4576040516394613e9960e01b815260040160405180910390fd5b6001600160a01b0384166001146123fc5781516123fc906001600160a01b0386169085614628565b61240682846146b2565b61240f82612f16565b600061241a83612f59565b90506124258361358a565b600061243087613401565b60c08086015160608088015160408051868152602081018e9052908101889052918201929092526080810191909152600060a08201529192507f0be79565c9c0f7b4144002bd9abeb6bd0e8f43023abae7066992c58bdff9397e9101610a82565b61249b823361451e565b6124cf6001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894163383614628565b604051631c57762b60e31b815260006004820152602481018290527f00000000000000000000000087664d190669a00ce699944c2485326d574ecd026001600160a01b03169063e2bbb158906044016020604051808303816000875af115801561253d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125619190615882565b50600082815260046020526040812061257b906003612e55565b90506125aa600361258b84613514565b6125959084615862565b60008681526004602052604090209190613612565b505050565b6000546001600160a01b031633146125da57604051634755657960e01b815260040160405180910390fd5b60408051633db1a4d360e01b815273cbca586bf9706706398164bb5eb8e48f220fe40891633db1a4d3916126379186918691907f000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea90600401615760565b60006040518083038186803b15801561264f57600080fd5b505af4158015612663573d6000803e3d6000fd5b5050505060008280602001905181019061267d9190615a68565b8051909150461461268d57600080fd5b602081015160405163ac46ae5160e01b8152600260048201526001600160a01b037f00000000000000000000000087664d190669a00ce699944c2485326d574ecd02811660248301527f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894811660448301527f000000000000000000000000b5284ed1606b91e0129182d55ee7ee31c31c920c166064820152608481019190915273cbca586bf9706706398164bb5eb8e48f220fe4089063ac46ae519060a40160006040518083038186803b15801561276457600080fd5b505af4158015610b17573d6000803e3d6000fd5b6000546001600160a01b031633146127a357604051634755657960e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f6b70829fcbe4891157f7a7496f9870927de3c8237adbe9cd39bae09b7382c4099060200161189b565b61283960405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b604051637d4b09b360e01b81526002600482015273cbca586bf9706706398164bb5eb8e48f220fe40890637d4b09b39060240161010060405180830381865af415801561288a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128ae9190615ab6565b905090565b6128bd863361451e565b600560008190526020527f458b30c2d72bfd2c6317304a4594ecbafe5f729d3111b65fdc3a33bd48e5432d546128f4908790612dc0565b5083600003612916576040516394613e9960e01b815260040160405180910390fd5b60006129233388886134b6565b905061292e81612f16565b600061293982612f59565b9050600061294783886130a0565b90506129546064836156e9565b811161295e575060005b60006129698a613401565b90507ecae5d4a40f5ab841867a076a796230392d8ce403d409f84f18377b49a602d5818b848760c0015188606001518d8d8d8d6040516129b199989796959493929190615b5c565b60405180910390a150505050505050505050565b604051630afc7a3960e31b81526005600482015260609073cbca586bf9706706398164bb5eb8e48f220fe408906357e3d1c890602401600060405180830381865af4158015612a18573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526128ae9190810190615bab565b612a4a833361451e565b600360005260056020527fa9bc9a3a348c357ba16b37005d7e6b3236198c0e939f4af8c5f19b8deeb8ebc054612a81908490612dc0565b5080600003612aa3576040516394613e9960e01b815260040160405180910390fd5b6000612ab03385856134b6565b9050612abb81612f16565b6000612ac682612f59565b90506000612ad483856130a0565b9050612ae16064836156e9565b8111612aeb575060005b6000612af687613401565b60c08086015160608088015160408051868152602081018e905290810188905291820192909252608081019190915260a081018890529192507f88ad2ade0e436c3cffc2b0d1b51fb1379e5bca5dd3aaef801b234b18aa87d2a99101610a82565b612baa6040518061010001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160001515815260200160008152602001600081525090565b60405163ee55e7b560e01b8152600360048083019190915260248201526044810183905273cbca586bf9706706398164bb5eb8e48f220fe4089063ee55e7b59060640161010060405180830381865af4158015612c0b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b89190615c50565b612c39843361451e565b600460005260056020527f3eec716f11ba9e820c81ca75eb978ffb45831ef8b7a53e5e422c26008e1ca6d554612c70908590612dc0565b506000848152600460205260408120612c929033908790610cba906002612e55565b9050612c9d81612f16565b6000612ca882612f59565b90506000612cb587613401565b90507fd8eb4847ae2a642c0c24f4336eb3ac86a4cac889da47bb554f95c4d6163d80ff8188848660c0015187606001518b8b8b604051610a82989796959493929190615cd2565b806001600160a01b03167f00000000000000000000000060138081198b75aaf15aca3a17ec7f5ffc5d46056001600160a01b0316636352211e846040518263ffffffff1660e01b8152600401612d5491815260200190565b602060405180830381865afa158015612d71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d959190615845565b6001600160a01b031614612dbc5760405163a03dca6f60e01b815260040160405180910390fd5b5050565b60405163659d6d9960e11b815260026004808301919091526024820152604481018390526064810182905260009073cbca586bf9706706398164bb5eb8e48f220fe4089063cb3adb3290608401602060405180830381865af4158015612e2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e4e9190615882565b9392505050565b60ff8116600090815260208390526040812054612e4e565b612e75614fba565b6001600160a01b0380851682526020820184905282166040820152612e9c60026001612e55565b60608201526001600160a01b0382166000908152600360205260409020612ec4906001612e55565b6001600160a01b031660808201526000838152600460205260409020612eeb906003612e55565b60a08201526000838152600460208190526040909120612f0a91612e55565b60c08201529392505050565b6040808201516001600160a01b0316600090815260036020819052919020612f3d91612e55565b60e08201526040810151612f5090614913565b61010090910152565b600080670de0b6b3a76400008360e00151670de0b6b3a764000085610100015186608001516001600160a01b0316631e01043988602001516040518263ffffffff1660e01b8152600401612faf91815260200190565b602060405180830381865afa158015612fcc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ff09190615882565b612ffa9190615d18565b61300491906156e9565b61300e9190615d18565b61301891906156e9565b90506000808460a0015112156130435761303e8460a0015161303990615d2f565b61439e565b613046565b60005b9050808210613099576130967f000000000000000000000000000000000000000000000000000000000000000660ff16601261308f8760a0015186614a9190919063ffffffff16565b9190614abe565b92505b5050919050565b60008261010001516000196130b591906156e9565b8210156108b85760808301516020840151604051631e01043960e01b815260048101919091526000916001600160a01b031690631e01043990602401602060405180830381865afa15801561310e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131329190615882565b90508083106131a95760008460a0015113156131a4576000670de0b6b3a7640000856101000151838661316591906158df565b61316f9190615d18565b61317991906156e9565b9050600061318a8660a0015161439e565b9050818111156131a15761319e82826158df565b93505b50505b61324f565b6000670de0b6b3a76400008560e00151670de0b6b3a764000087610100015187866131d491906158df565b6131de9190615d18565b6131e891906156e9565b6131f29190615d18565b6131fc91906156e9565b905060008560a00151126132215760a085015161321a908290614a91565b925061324d565b60006132348660a0015161303990615d2f565b90508082111561324b5761324881836158df565b93505b505b505b8115613287576132848260ff7f0000000000000000000000000000000000000000000000000000000000000006166012614abe565b91505b5092915050565b60008083608001516001600160a01b0316631e01043985602001516040518263ffffffff1660e01b81526004016132c791815260200190565b602060405180830381865afa1580156132e4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133089190615882565b90506000670de0b6b3a76400008560e00151670de0b6b3a7640000876101000151856133349190615d18565b61333e91906156e9565b6133489190615d18565b61335291906156e9565b90506000808660a00151126133785760a0860151613371908390614a91565b9050613396565b6133898660a0015161303990615d2f565b61339390836158df565b90505b848111156133d4576133d160ff7f000000000000000000000000000000000000000000000000000000000000000616601261308f88856158df565b93505b50505092915050565b6001600160a01b0381165b60ff909216600090815260209390935250604090912055565b60008061341060026005612e55565b905061341d600182615d4b565b9050613435600260056001600160801b038416613612565b600083815260046020526040812061344e906001612e55565b905061345b600182615d4b565b60008581526004602052604090209091506134819060016001600160801b038416613612565b60006134ad6001600160801b0383166fffffffffffffffffffffffffffffffff19608086901b166156fd565b95945050505050565b6134be614fba565b6134c9848484612e6d565b9050612e4e8383614af1565b900390565b6000806134e885858561354a565b905060008512801561350457508461350182858761354a565b14155b15611f80576134ad6001826158bf565b60006001600160ff1b0382111561354657604051632a26bd1360e01b8152600481018390526024015b60405180910390fd5b5090565b60008183146135825761355e83600a615e4f565b61356983600a615e4f565b6135739086615e5b565b61357d9190615e8b565b611f80565b509192915050565b606081015161359e90600290600190613612565b60408082015160208084015160009081526004909152919091206135c4916002906133dd565b60a081015160208083015160009081526004909152604090206135e991600390613612565b60c081015160208083015160009081526004918290526040902061360f92909190613612565b50565b806133e8565b600082815260046020526040902081906001600160801b0382169061363e906001612e55565b1461365c576040516302e8145360e61b815260040160405180910390fd5b6125aa600161366b8382615d4b565b600086815260046020526040902091906001600160801b0316613612565b60008061369585611f88565b90925090506136a660028042613612565b6136b36002600385613612565b6136c06002600484613612565b60008581526004602052604090206136da90600586613612565b60008581526004602052604090206136f490600683613612565b600085815260046020526040902061167e90600784613612565b60007f000000000000000000000000cf82aaa68b3868fd2f280f9b211edcdea9f4770d6001600160a01b031663c19d93fb6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561376e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137929190615eb9565b1561379c57600080fd5b60006137c960027f0000000000000000000000000000000000000000000000000000000000000006615ed6565b6137d490600a615eef565b84925090506137ed670de0b6b3a76400006000196156e9565b821080156137ff575060008560a00151125b156138bb577f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b031685604001516001600160a01b031603613863576138528560a0015161303990615d2f565b61385c90836156fd565b91506138bb565b6064856101000151670de0b6b3a76400006138858860a0015161303990615d2f565b61388f9190615d18565b61389991906156e9565b6138a4906069615d18565b6138ae91906156e9565b6138b890836156fd565b91505b60808501516020860151604051637cbc237360e01b81526001600160a01b0390921691637cbc2373916138fb918690600401918252602082015260400190565b6020604051808303816000875af115801561391a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061393e9190615882565b9150600080600086851115613b4e57600061395988876158df565b905060007f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b03168a604001516001600160a01b0316036139ad5750806139a681886158df565b9650613b22565b60016001600160a01b03168a604001516001600160a01b031603613a6d576000807f0000000000000000000000009a34b3810d422373ba5128ffee880235003f5cac6001600160a01b031663f29216e2856040518263ffffffff1660e01b8152600401604080518083038185885af1158015613a2d573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613a52919061589b565b9093508392509050613a64818a6158df565b98505050613b22565b60408a8101519051633e87f49360e01b81526001600160a01b0391821660048201526024810184905260009182917f0000000000000000000000009a34b3810d422373ba5128ffee880235003f5cac90911690633e87f4939060440160408051808303816000875af1158015613ae7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b0b919061589b565b9093508392509050613b1d818a6158df565b985050505b613b2c81866156fd565b9450613b3781613514565b8a60a001818151613b489190615862565b90525050505b600085118015613b62575060008860a00151125b15613d9f576000613b7a8960a0015161303990615d2f565b905060007f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b03168a604001516001600160a01b031603613bde57818710613bd5575080613bce81886158df565b9650613d73565b50600095613d73565b85821015613bea578591505b60016001600160a01b03168a604001516001600160a01b031603613cb7576000807f0000000000000000000000009a34b3810d422373ba5128ffee880235003f5cac6001600160a01b0316637661f4f68a866040518363ffffffff1660e01b8152600401613c5a91815260200190565b604080518083038185885af1158015613c77573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613c9c919061589b565b9093508392509050613cae818a6158df565b98505050613d73565b60408a810151905163bf441a5960e01b81526001600160a01b039182166004820152602481018490526044810189905260009182917f0000000000000000000000009a34b3810d422373ba5128ffee880235003f5cac9091169063bf441a599060640160408051808303816000875af1158015613d38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d5c919061589b565b9093508392509050613d6e818a6158df565b985050505b613d7d81866156fd565b9450613d8881613514565b8a60a001818151613d999190615862565b90525050505b60008860a001511315613fa8576000613dc2670de0b6b3a76400006000196156e9565b8810613ddc57613dd58960a0015161439e565b9050613e42565b7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b031689604001516001600160a01b0316148015613e2057508786105b15613e4257613e3f613e358a60a0015161439e565b611bf0888b6158df565b90505b8015613fa657600084821115613f595760006001600160a01b037f00000000000000000000000087664d190669a00ce699944c2485326d574ecd0216637cbc237382613e8e89876158df565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af1158015613ed1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ef59190615882565b9050613f0186846158df565b811015613f43578815613f2a5780613f1987856158df565b613f2391906158df565b9350613f43565b6040516320b7831f60e11b815260040160405180910390fd5b613f4d81876156fd565b91506000955050613f68565b5080613f6581866158df565b94505b613f7281856156fd565b9350613f7d83613514565b613f8682613514565b613f909190615862565b8a60a001818151613fa191906158bf565b905250505b505b821561404257604051631c57762b60e31b815260006004820152602481018490527f00000000000000000000000087664d190669a00ce699944c2485326d574ecd026001600160a01b03169063e2bbb158906044016020604051808303816000875af115801561401c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140409190615882565b505b81156142e35785156140d8577f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b031688604001516001600160a01b03160361409c5761409582866156fd565b94506142e3565b87516140d3906001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388941690846143db565b6142e3565b7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b031688604001516001600160a01b03160361411f5761409582866156fd565b838210156141765761416c6141637f000000000000000000000000000000000000000000000000000000000000000660ff16601261415c86613514565b919061354a565b60608a01510190565b60608901526142e3565b60016001600160a01b031688604001516001600160a01b03160361423757604051632cb4d9d960e11b8152600481018390526000907f0000000000000000000000009a34b3810d422373ba5128ffee880235003f5cac6001600160a01b031690635969b3b29060240160408051808303816000875af11580156141fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614221919061589b565b915061422f905081876156fd565b9550506142e3565b604088810151905163045608af60e01b81526001600160a01b039182166004820152602481018490526000917f0000000000000000000000009a34b3810d422373ba5128ffee880235003f5cac169063045608af9060440160408051808303816000875af11580156142ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142d1919061589b565b91506142df905081876156fd565b9550505b84156143065787516040890151614306916001600160a01b0390911690876143db565b80156143935787516040516340c10f1960e01b81526001600160a01b039182166004820152602481018390527f000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f429909116906340c10f1990604401600060405180830381600087803b15801561437a57600080fd5b505af115801561438e573d6000803e3d6000fd5b505050505b505050509392505050565b60008082121561354657604051632a33bb3160e01b81526004810183905260240161353d565b6000818311156143d45781612e4e565b5090919050565b60006143e684614bf3565b90506000196001600160a01b03851601614474576000836001600160a01b03168360405160006040518083038185875af1925050503d8060008114614447576040519150601f19603f3d011682016040523d82523d6000602084013e61444c565b606091505b505090508061446e5760405163e277d13760e01b815260040160405180910390fd5b50614488565b6144886001600160a01b0385168484614c7a565b600061449385614bf3565b905061449f83826156fd565b821461167e5760405163f4d4667360e01b815260040160405180910390fd5b60405163901b479760e01b815260026004808301919091526024820152604481018390526001600160a01b038216606482015273cbca586bf9706706398164bb5eb8e48f220fe4089063901b479790608401611481565b6133e881614cdd565b806001600160a01b03167f00000000000000000000000067bdd68f20a1bb06f487d29b26ad63e162a2f8286001600160a01b0316636352211e846040518263ffffffff1660e01b815260040161457691815260200190565b602060405180830381865afa158015614593573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145b79190615845565b6001600160a01b031614612dbc57604051639289a15b60e01b815260040160405180910390fd5b6001600160a01b0381166000908152600360205260408120614601906001612e55565b6001600160a01b03160361360f57604051634033aec960e01b815260040160405180910390fd5b6000196001600160a01b0384160161465a573481146125aa57604051630fc9bd0f60e11b815260040160405180910390fd5b600061466584614bf3565b905061467c6001600160a01b038516843085614cf7565b600061468785614bf3565b905061469383836156fd565b811461167e57604051630fc9bd0f60e11b815260040160405180910390fd5b7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b031682604001516001600160a01b0316036147ef576000670de0b6b3a76400006147247f00000000000000000000000000000000000000000000000002c68af0bb14000084615d18565b61472e91906156e9565b905061473a81836158df565b604051631c57762b60e31b815260006004820152602481018390529092507f00000000000000000000000087664d190669a00ce699944c2485326d574ecd026001600160a01b03169063e2bbb158906044016020604051808303816000875af11580156147ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147cf9190615882565b506147d981613514565b8360a0018181516147ea9190615862565b905250505b60016001600160a01b031682604001516001600160a01b0316036148905781608001516001600160a01b031663e2bbb158828460200151846040518463ffffffff1660e01b815260040161484d929190918252602082015260400190565b60206040518083038185885af115801561486b573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906125aa9190615882565b60808201516020830151604051631c57762b60e31b81526001600160a01b039092169163e2bbb158916148d0918590600401918252602082015260400190565b6020604051808303816000875af11580156148ef573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125aa9190615882565b60007f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b0316826001600160a01b03160361495d5750670de0b6b3a7640000919050565b6000614971836001600160a01b0316614d2f565b9050614a678160ff167f000000000000000000000000000000000000000000000000000000000000000660ff1661308f7f000000000000000000000000bca4439e99091afb297ecb4c5672357e467664f26001600160a01b03166369843940614a086002600360008c6001600160a01b03166001600160a01b03168152602001908152602001600020614dac90919063ffffffff16565b6040518263ffffffff1660e01b8152600401614a2691815260200190565b602060405180830381865afa158015614a43573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130399190615882565b915081600003614a8a5760405163089112d560e21b815260040160405180910390fd5b505b919050565b6000808212614aab57614aa482846156fd565b90506108b8565b614ab482615d2f565b614aa490846158df565b600081831461358257614ad283600a615e4f565b614add83600a615e4f565b614ae79086615d18565b61357d91906156e9565b6000828152600460205260408120614b0a906002612e55565b90506001600160a01b03811615801590614b365750816001600160a01b0316816001600160a01b031614155b156125aa576001600160a01b0381166000908152600360205260408120614b5e906001612e55565b6001600160a01b0316635d988967856040518263ffffffff1660e01b8152600401614b8b91815260200190565b602060405180830381865afa158015614ba8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614bcc9190615882565b90508015614bed57604051634033aec960e01b815260040160405180910390fd5b50505050565b60006001600160a01b038216600114614c73576040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015614c4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c6e9190615882565b6108b8565b4792915050565b6040516001600160a01b0383166024820152604481018290526125aa90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614dc2565b600081614ceb576000614cee565b60015b60ff1692915050565b6040516001600160a01b0380851660248301528316604482015260648101829052614bed9085906323b872dd60e01b90608401614ca6565b60006001600160a01b038216600114614da457816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015614d80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c6e9190615efe565b601292915050565b60ff166000908152602091909152604090205490565b6000614e17826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614e979092919063ffffffff16565b9050805160001480614e38575080806020019051810190614e389190615eb9565b6125aa5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161353d565b6060611f80848460008585600080866001600160a01b03168587604051614ebe9190615f21565b60006040518083038185875af1925050503d8060008114614efb576040519150601f19603f3d011682016040523d82523d6000602084013e614f00565b606091505b5091509150614f1187838387614f1c565b979650505050505050565b60608315614f8b578251600003614f84576001600160a01b0385163b614f845760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161353d565b5081611f80565b611f808383815115614fa05781518083602001fd5b8060405162461bcd60e51b815260040161353d9190615f3d565b60405180610120016040528060006001600160a01b031681526020016000815260200160006001600160a01b031681526020016000815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b038116811461360f57600080fd5b60006020828403121561504857600080fd5b8135612e4e81615021565b60008060006060848603121561506857600080fd5b83359250602084013561507a81615021565b929592945050506040919091013590565b60008083601f84011261509d57600080fd5b5081356001600160401b038111156150b457600080fd5b6020830191508360208260051b85010111156150cf57600080fd5b9250929050565b801515811461360f57600080fd5b600080600080600080600060c0888a0312156150ff57600080fd5b87359650602088013561511181615021565b9550604088013594506060880135935060808801356001600160401b0381111561513a57600080fd5b6151468a828b0161508b565b90945092505060a088013561515a816150d6565b8091505092959891949750929550565b634e487b7160e01b600052604160045260246000fd5b60405161014081016001600160401b03811182821017156151a3576151a361516a565b60405290565b60405161010081016001600160401b03811182821017156151a3576151a361516a565b604051601f8201601f191681016001600160401b03811182821017156151f4576151f461516a565b604052919050565b600082601f83011261520d57600080fd5b81356001600160401b038111156152265761522661516a565b615239601f8201601f19166020016151cc565b81815284602083860101111561524e57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806040838503121561527e57600080fd5b82356001600160401b038082111561529557600080fd5b6152a1868387016151fc565b935060208501359150808211156152b757600080fd5b506152c4858286016151fc565b9150509250929050565b600080604083850312156152e157600080fd5b8235915060208301356152f3816150d6565b809150509250929050565b60006020828403121561531057600080fd5b5035919050565b8151815260208083015161014083019161533b908401826001600160a01b03169052565b5060408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151818401525092915050565b600080600080608085870312156153ac57600080fd5b8435935060208501356153be81615021565b92506040850135915060608501356153d5816150d6565b939692955090935050565b600080604083850312156153f357600080fd5b50508035926020909101359150565b81516001600160a01b031681526101e08101602083015161542e60208401826001600160a01b03169052565b50604083015161544960408401826001600160a01b03169052565b50606083015161546460608401826001600160a01b03169052565b50608083015161547f60808401826001600160a01b03169052565b5060a083015161549a60a08401826001600160a01b03169052565b5060c08301516154b560c08401826001600160a01b03169052565b5060e08301516154d060e08401826001600160a01b03169052565b506101008381015190830152610120808401519083015261014080840151908301526101608084015190830152610180808401516001600160a01b0381168285015250506101a0838101516001600160a01b0381168483015250506101c0838101516001600160a01b038116848301525b505092915050565b60008060008060008060a0878903121561556257600080fd5b86359550602087013561557481615021565b9450604087013593506060870135925060808701356001600160401b0381111561559d57600080fd5b6155a989828a0161508b565b979a9699509497509295939492505050565b6020808252825182820181905260009190848201906040850190845b818110156155f3578351835292840192918401916001016155d7565b50909695505050505050565b6000806000806060858703121561561557600080fd5b843593506020850135925060408501356001600160401b0381111561563957600080fd5b6156458782880161508b565b95989497509550505050565b8051614a8c81615021565b60006060828403121561566e57600080fd5b604051606081018181106001600160401b03821117156156905761569061516a565b604052825161569e81615021565b8152602083810151908201526040928301519281019290925250919050565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000826156f8576156f86156bd565b500490565b808201808211156108b8576108b86156d3565b60005b8381101561572b578181015183820152602001615713565b50506000910152565b6000815180845261574c816020860160208601615710565b601f01601f19169290920160200192915050565b6080815260006157736080830187615734565b82810360208401526157858187615734565b604084019590955250506001600160a01b039190911660609091015292915050565b600060e082840312156157b957600080fd5b60405160e081018181106001600160401b03821117156157db576157db61516a565b60405282516157e981615021565b815260208301516157f981615021565b6020820152604083015161580c81615021565b80604083015250606083015160608201526080830151608082015260a083015160a082015260c083015160c08201528091505092915050565b60006020828403121561585757600080fd5b8151612e4e81615021565b8082018281126000831280158216821582161715615541576155416156d3565b60006020828403121561589457600080fd5b5051919050565b600080604083850312156158ae57600080fd5b505080516020909101519092909150565b8181036000831280158383131683831282161715613287576132876156d3565b818103818111156108b8576108b86156d3565b6000610140828403121561590557600080fd5b61590d615180565b8251815261591d60208401615651565b602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152506101208084015181830152508091505092915050565b600060c0828403121561599257600080fd5b60405160c081018181106001600160401b03821117156159b4576159b461516a565b8060405250825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a08201528091505092915050565b600060a08284031215615a0b57600080fd5b60405160a081018181106001600160401b0382111715615a2d57615a2d61516a565b806040525082518152602083015160208201526040830151604082015260608301516060820152608083015160808201528091505092915050565b600060408284031215615a7a57600080fd5b604051604081018181106001600160401b0382111715615a9c57615a9c61516a565b604052825181526020928301519281019290925250919050565b60006101008284031215615ac957600080fd5b615ad16151a9565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201528091505092915050565b8183526000602080850194508260005b85811015615b5157813587529582019590820190600101615b35565b509495945050505050565b60006101008b83528a60208401528960408401528860608401528760808401528660a08401528560c08401528060e0840152615b9b8184018587615b25565b9c9b505050505050505050505050565b60006020808385031215615bbe57600080fd5b82516001600160401b0380821115615bd557600080fd5b818501915085601f830112615be957600080fd5b815181811115615bfb57615bfb61516a565b8060051b9150615c0c8483016151cc565b8181529183018401918481019088841115615c2657600080fd5b938501935b83851015615c4457845182529385019390850190615c2b565b98975050505050505050565b60006101008284031215615c6357600080fd5b615c6b6151a9565b825181526020830151615c7d81615021565b8060208301525060408301516040820152606083015160608201526080830151608082015260a0830151615cb0816150d6565b60a082015260c0838101519082015260e0928301519281019290925250919050565b8881528760208201528660408201528560608201528460808201528360a082015260e060c08201526000615d0a60e083018486615b25565b9a9950505050505050505050565b80820281158282048414176108b8576108b86156d3565b6000600160ff1b8201615d4457615d446156d3565b5060000390565b6001600160801b03818116838216019080821115613287576132876156d3565b600181815b80851115615da6578160001904821115615d8c57615d8c6156d3565b80851615615d9957918102915b93841c9390800290615d70565b509250929050565b600082615dbd575060016108b8565b81615dca575060006108b8565b8160018114615de05760028114615dea57615e06565b60019150506108b8565b60ff841115615dfb57615dfb6156d3565b50506001821b6108b8565b5060208310610133831016604e8410600b8410161715615e29575081810a6108b8565b615e338383615d6b565b8060001904821115615e4757615e476156d3565b029392505050565b6000612e4e8383615dae565b80820260008212600160ff1b84141615615e7757615e776156d3565b81810583148215176108b8576108b86156d3565b600082615e9a57615e9a6156bd565b600160ff1b821460001984141615615eb457615eb46156d3565b500590565b600060208284031215615ecb57600080fd5b8151612e4e816150d6565b60ff82811682821603908111156108b8576108b86156d3565b6000612e4e60ff841683615dae565b600060208284031215615f1057600080fd5b815160ff81168114612e4e57600080fd5b60008251615f33818460208701615710565b9190910192915050565b602081526000612e4e602083018461573456fea2646970667358221220e2baf2c8c978e5b836b2d7d23da6f71327c5bd537b9b46c1a3da24f0c128d04b64736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000060138081198b75aaf15aca3a17ec7f5ffc5d460500000000000000000000000067bdd68f20a1bb06f487d29b26ad63e162a2f828000000000000000000000000bca4439e99091afb297ecb4c5672357e467664f20000000000000000000000009a34b3810d422373ba5128ffee880235003f5cac00000000000000000000000087664d190669a00ce699944c2485326d574ecd02000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f42900000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea00000000000000000000000000000000000000000000000002c68af0bb14000000000000000000000000000000000000000000000000000006f05b59d3b200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001dcd6500000000000000000000000000b5284ed1606b91e0129182d55ee7ee31c31c920c000000000000000000000000d4e08c940ddec162c2d8f3034c75c3e08f1f6032000000000000000000000000cf82aaa68b3868fd2f280f9b211edcdea9f4770d
-----Decoded View---------------
Arg [0] : p (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
-----Encoded View---------------
15 Constructor Arguments found :
Arg [0] : 00000000000000000000000060138081198b75aaf15aca3a17ec7f5ffc5d4605
Arg [1] : 00000000000000000000000067bdd68f20a1bb06f487d29b26ad63e162a2f828
Arg [2] : 000000000000000000000000bca4439e99091afb297ecb4c5672357e467664f2
Arg [3] : 0000000000000000000000009a34b3810d422373ba5128ffee880235003f5cac
Arg [4] : 00000000000000000000000087664d190669a00ce699944c2485326d574ecd02
Arg [5] : 000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f429
Arg [6] : 00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894
Arg [7] : 000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea
Arg [8] : 00000000000000000000000000000000000000000000000002c68af0bb140000
Arg [9] : 00000000000000000000000000000000000000000000000006f05b59d3b20000
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [11] : 000000000000000000000000000000000000000000000000000000001dcd6500
Arg [12] : 000000000000000000000000b5284ed1606b91e0129182d55ee7ee31c31c920c
Arg [13] : 000000000000000000000000d4e08c940ddec162c2d8f3034c75c3e08f1f6032
Arg [14] : 000000000000000000000000cf82aaa68b3868fd2f280f9b211edcdea9f4770d
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 35 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.