Source Code
Overview
ETH Balance
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
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();
error InsufficientExecutionFee();
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 = _getData(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, bToken);
_getExParams(data);
uint256 oldLiquidity = _getDTokenLiquidity(data);
uint256 newLiquidity = _getDTokenLiquidityWithRemove(data, bAmount);
if (newLiquidity <= oldLiquidity / 100) {
newLiquidity = 0;
}
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 = _getData(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;
}
/**
* @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 = _getData(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 = _getData(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 = _getData(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 = _getData(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 = _getData(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;
if (v.bAmountToRemove != 0) {
_getExParams(data);
bAmountRemoved = _transferOut(data, v.liquidity == 0 ? type(uint256).max : v.bAmountToRemove, false);
}
_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,
data.bToken,
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 = _getData(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, 128, dChainEventSigner);
IGateway.VarOnExecuteLiquidate memory v = abi.decode(eventData, (IGateway.VarOnExecuteLiquidate));
// Cumulate unsettled PNL to b0Amount
Data memory data = _getData(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
{
if (lpPnl <= minLiquidationReward) {
reward = minLiquidationReward;
} else {
reward = SafeMath.min(
(lpPnl - minLiquidationReward) * liquidationRewardCutRatio / ONE + minLiquidationReward,
maxLiquidationReward
);
}
uint256 uReward = reward.itou();
if (uReward <= b0AmountIn) {
tokenB0.transferOut(msg.sender, uReward);
b0AmountIn -= uReward;
} else {
uint256 b0Redeemed = vault0.redeem(uint256(0), uReward - b0AmountIn);
tokenB0.transferOut(msg.sender, b0AmountIn + b0Redeemed);
reward = (b0AmountIn + b0Redeemed).utoi();
b0AmountIn = 0;
}
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);
_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) {
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(uint256 dTokenId, address to) internal {
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);
}
}
// @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).rescale(decimalsB0, 18);
}
}
} else {
uint256 b0Excessive = (bAmountInVault - bAmount) * data.bPrice / UONE * data.collateralFactor / UONE; // discounted
if (data.b0Amount >= 0) {
liquidity = b0Excessive.add(data.b0Amount).rescale(decimalsB0, 18);
} else {
uint256 b0Shortage = (-data.b0Amount).itou();
if (b0Excessive > b0Shortage) {
liquidity = (b0Excessive - b0Shortage).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();
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;
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;
}
}// 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
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 {
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": "0x7b8bcf00def58b50620b2c253f3a97ee51f44683"
}
}
}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":"InsufficientExecutionFee","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":[{"internalType":"address","name":"bToken","type":"address"},{"internalType":"address","name":"vault","type":"address"},{"internalType":"bytes32","name":"oracleId","type":"bytes32"},{"internalType":"uint256","name":"collateralFactor","type":"uint256"}],"name":"addBToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"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":"address","name":"bToken","type":"address"}],"name":"delBToken","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"}],"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":"bToken","type":"address"},{"internalType":"uint8","name":"idx","type":"uint8"},{"internalType":"bytes32","name":"value","type":"bytes32"}],"name":"setBTokenParameter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"dChainExecutionFeePerRequest","type":"uint256"}],"name":"setDChainExecutionFeePerRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"actionId","type":"uint256"},{"internalType":"uint256","name":"executionFee","type":"uint256"}],"name":"setExecutionFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"setImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6102806040523480156200001257600080fd5b506040516200660a3803806200660a833981016040819052620000359162000223565b600080546001600160a01b031916339081179091556040519081527f71614071b88dee5e0b2ae578a9dd7b2ebbe9ae832ba419dc0242cd065a290b6c9060200160405180910390a180516001600160a01b0390811660809081526020830151821660a09081526040840151831660c09081526060850151841660e052918401518316610100528301518216610120528201805182166101405251620000db911662000146565b60ff1661018090815260e08201516001600160a01b039081166101609081526101008401516101a09081526101208501516101c09081526101408601516101e0529185015161020052928401518216610220529183015181166102405291015116610260526200036e565b60006001600160a01b038216600114620001c557816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000199573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001bf919062000342565b620001c8565b60125b92915050565b6040516101e081016001600160401b03811182821017156200020057634e487b7160e01b600052604160045260246000fd5b60405290565b80516001600160a01b03811681146200021e57600080fd5b919050565b60006101e082840312156200023757600080fd5b62000241620001ce565b6200024c8362000206565b81526200025c6020840162000206565b60208201526200026f6040840162000206565b6040820152620002826060840162000206565b6060820152620002956080840162000206565b6080820152620002a860a0840162000206565b60a0820152620002bb60c0840162000206565b60c0820152620002ce60e0840162000206565b60e082015261010083810151908201526101208084015190820152610140808401519082015261016080840151908201526101806200030f81850162000206565b908201526101a06200032384820162000206565b908201526101c06200033784820162000206565b908201529392505050565b6000602082840312156200035557600080fd5b815160ff811681146200036757600080fd5b9392505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051615ff462000616600039600061387b0152600081816106ee0152610e900152600081816106c501526127a401526000818161069e015261104f01526000818161067701528181610f8b01528181610fb30152610fdc01526000818161065001526110060152600081816106290152614783015260008181610cf701528181610e2f0152818161127f01528181611c41015281816121a4015281816131ab015281816132f20152818161338f015281816133f50152818161390e0152818161429d0152614afe01526000818161060101528181610b9f01528181611b26015281816120a001526126b30152600081816105d901528181610dc0015281816110970152818161118c0152818161198c015281816125b90152818161277c0152818161396f01528181613ac801528181613ce901528181613f47015281816141be0152818161421a01528181614248015281816147380152614a990152600081816105b1015281816119dc01526144a2015260008181610589015281816110db015281816111fd015281816119b4015281816125910152818161275401528181613fcc0152818161413901526147de01526000818161056101528181610e680152818161254101528181613b3901528181613c0601528181613d7601528181613e570152818161431b01526143d301526000818161053a015281816125690152614b2501526000818161051201528181610c1601528181610eb801528181611395015281816114a50152818161166401528181611de10152818161212901526145b40152600081816104ed0152818161147d01528181611bc6015281816123290152612d8c0152615ff46000f3fe6080604052600436106101d85760003560e01c80637e9459ba11610102578063d784d42611610095578063f7c60d4411610064578063f7c60d441461080b578063f802a6971461081e578063f851a440146108a5578063fa321c26146108c557600080fd5b8063d784d42614610740578063db40156614610760578063e1faff56146107d6578063e926bc9d146107e957600080fd5b8063aaba0398116100d1578063aaba039814610490578063ab1aac60146104a3578063c323acb6146104c3578063cd18dde31461072057600080fd5b80637e9459ba146103fa5780638769da2d1461041b5780639b1a2e9414610450578063a6445bb51461047057600080fd5b80633e0c7fc11161017a578063619cf2be11610149578063619cf2be1461036d578063702e6c061461039a578063704b6c02146103ba57806370a1848c146103da57600080fd5b80633e0c7fc1146102d557806349006539146102f55780635c60da1b146103155780635c8e13a61461034d57600080fd5b80631c58d9c1116101b65780631c58d9c11461025557806323cc0ebf1461027557806327a029141461029557806338f438a3146102b557600080fd5b8063068689cd146101dd5780630c8aeb561461022d57806316890fc214610242575b600080fd5b3480156101e957600080fd5b506101fd6101f836600461506e565b6108d8565b6040805182516001600160a01b031681526020808401519082015291810151908201526060015b60405180910390f35b61024061023b36600461508b565b61097a565b005b61024061025036600461511c565b610a9e565b34801561026157600080fd5b506102406102703660046152a3565b610b2b565b34801561028157600080fd5b50610240610290366004615306565b611456565b3480156102a157600080fd5b506102406102b0366004615336565b61152d565b3480156102c157600080fd5b506102406102d036600461506e565b61159e565b3480156102e157600080fd5b506102406102f0366004615358565b61163f565b34801561030157600080fd5b50610240610310366004615380565b611775565b34801561032157600080fd5b50600154610335906001600160a01b031681565b6040516001600160a01b039091168152602001610224565b34801561035957600080fd5b5061024061036836600461506e565b61181f565b34801561037957600080fd5b5061038d610388366004615358565b611891565b60405161022491906153b0565b3480156103a657600080fd5b506102406103b5366004615358565b611975565b3480156103c657600080fd5b506102406103d536600461506e565b611a32565b3480156103e657600080fd5b506102406103f53660046152a3565b611ab2565b61040d61040836600461542f565b611dc2565b604051908152602001610224565b34801561042757600080fd5b5061043b610436366004615358565b611f9b565b60408051928352602083019190915201610224565b34801561045c57600080fd5b5061024061046b3660046152a3565b61202c565b34801561047c57600080fd5b5061024061048b366004615358565b6122a2565b61024061049e36600461508b565b61230c565b3480156104af57600080fd5b506102406104be366004615479565b6124f8565b3480156104cf57600080fd5b50604080516101e08101825260006101c08201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f00000000000000000000000000000000000000000000000000000000000000008116828401527f0000000000000000000000000000000000000000000000000000000000000000811660608301527f0000000000000000000000000000000000000000000000000000000000000000811660808301527f0000000000000000000000000000000000000000000000000000000000000000811660a08301527f0000000000000000000000000000000000000000000000000000000000000000811660c08301527f0000000000000000000000000000000000000000000000000000000000000000811660e08301527f00000000000000000000000000000000000000000000000000000000000000006101008301527f00000000000000000000000000000000000000000000000000000000000000006101208301527f00000000000000000000000000000000000000000000000000000000000000006101408301527f00000000000000000000000000000000000000000000000000000000000000006101608301527f000000000000000000000000000000000000000000000000000000000000000081166101808301527f0000000000000000000000000000000000000000000000000000000000000000166101a0820152905161022491906154bf565b34801561072c57600080fd5b5061024061073b3660046152a3565b612653565b34801561074c57600080fd5b5061024061075b36600461506e565b6127f5565b34801561076c57600080fd5b5061077561286e565b6040516102249190600061010082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b6102406107e4366004615602565b612930565b3480156107f557600080fd5b506107fe612a42565b6040516102249190615674565b61024061081936600461508b565b612abd565b34801561082a57600080fd5b5061083e610839366004615358565b612bd4565b6040516102249190815181526020808301516001600160a01b03169082015260408083015190820152606080830151908201526080808301519082015260a08083015115159082015260c0808301519082015260e091820151918101919091526101000190565b3480156108b157600080fd5b50600054610335906001600160a01b031681565b6102406108d33660046156b8565b612cac565b604080516060810182526000808252602082018190528183015290516340d24f1f60e11b8152600360048201526001600160a01b0383166024820152737b8bcf00def58b50620b2c253f3a97ee51f44683906381a49e3e90604401606060405180830381865af4158015610950573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109749190615715565b92915050565b6109848333612d80565b600260005260056020527f89832631fb3c3307a103ba2c84ab569c64d6182a18893dcd163f0f1c2090733a546109bb908490612e44565b50806000036109dd576040516394613e9960e01b815260040160405180910390fd5b60006109ea338585612f68565b90506109f581613070565b6000610a00826130b0565b90506000610a0e83856131f7565b9050610a1b6064836157a2565b8111610a25575060005b6000610a308761342f565b60c08086015160608088015160408051868152602081018e905290810188905291820192909252608081019190915260a081018890529192507f0be79565c9c0f7b4144002bd9abeb6bd0e8f43023abae7066992c58bdff9397e91015b60405180910390a150505050505050565b6000196001600160a01b03871601610b0857600460005260056020527f3eec716f11ba9e820c81ca75eb978ffb45831ef8b7a53e5e422c26008e1ca6d55434610ae782886157b6565b1115610b06576040516394613e9960e01b815260040160405180910390fd5b505b610b1487878784611dc2565b9650610b2287858585612cac565b50505050505050565b600154600160a01b900460ff1615610b56576040516325dbe6e160e21b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b179055604051633db1a4d360e01b8152737b8bcf00def58b50620b2c253f3a97ee51f4468390633db1a4d390610bc790859085906080907f000000000000000000000000000000000000000000000000000000000000000090600401615819565b60006040518083038186803b158015610bdf57600080fd5b505af4158015610bf3573d6000803e3d6000fd5b50505050600082806020019051810190610c0d9190615860565b90506000610ccc7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636352211e84602001516040518263ffffffff1660e01b8152600401610c6691815260200190565b602060405180830381865afa158015610c83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca791906158c5565b6020848101516000818152600483526040808220600283529093529190912054612f68565b90506000610ceb8260c00151846040015161350390919063ffffffff16565b9050610d1c81601260ff7f000000000000000000000000000000000000000000000000000000000000000016613508565b8260a001818151610d2d91906158e2565b90525060408381015160c0840152608083015160208401519151637cbc237360e01b81526004810192909252600019602483015260009182916001600160a01b031690637cbc2373906044016020604051808303816000875af1158015610d98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dbc9190615902565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031684604001516001600160a01b031603610e0c57610e0581836157b6565b9150610f6a565b604084810151602086015160a0870151606089015193516330706e4160e01b81527f000000000000000000000000000000000000000000000000000000000000000060ff1660048201526001600160a01b0393841660248201527f0000000000000000000000000000000000000000000000000000000000000000841660448201527f0000000000000000000000000000000000000000000000000000000000000000841660648201527f0000000000000000000000000000000000000000000000000000000000000000909316608484015260a483019190915260c482015260e48101839052610104810191909152737b8bcf00def58b50620b2c253f3a97ee51f44683906330706e419061012401602060405180830381865af4158015610f39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5d9190615902565b610f6790836157b6565b91505b5060008360a00151610f7b83613542565b610f8591906158e2565b905060007f00000000000000000000000000000000000000000000000000000000000000008213610fd757507f0000000000000000000000000000000000000000000000000000000000000000611076565b6110737f0000000000000000000000000000000000000000000000000000000000000000670de0b6b3a76400007f000000000000000000000000000000000000000000000000000000000000000061102f838761591b565b611039919061593b565b611043919061596b565b61104d91906158e2565b7f0000000000000000000000000000000000000000000000000000000000000000613578565b90505b60006110818261358f565b90508381116110cf576110be6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633836135b5565b6110c88185615999565b93506111cd565b60006001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016637cbc23738261110b8886615999565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af115801561114e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111729190615902565b90506111b33361118283886157b6565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691906135b5565b6111c56111c082876157b6565b613542565b925060009450505b6111d7828461591b565b925050821561127457604051631c57762b60e31b815260006004820152602481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e2bbb158906044016020604051808303816000875af115801561124e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112729190615902565b505b6112af6112a68360ff7f0000000000000000000000000000000000000000000000000000000000000000166012613698565b60608701510190565b6060860152600060a08601526112c4856136d8565b6020868101805160009081526004808452604080832060098085529086528184205485518552838752828520600a8087529088528386205487518752858952848720938752928852838620869055955185529286528184209484529385528220829055600790915260029092527facd8ef244210bb6898e73c48bf820ed8ecc857a3bab8d79c10e4fa92b1e9ca655461135d82846157b6565b6113679082615999565b90506113766002600783613760565b5050506020860151604051630852cd8d60e31b815260048101919091527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906342966c6890602401600060405180830381600087803b1580156113e157600080fd5b505af11580156113f5573d6000803e3d6000fd5b505087516020808a0151604080519384529183015281018590527f8c3b5c5c5dddce5cc5abfa2cc66ec5dd06f76f9e9bb21debb1a85ad88fdbdb6f9250606001905060405180910390a150506001805460ff60a01b19169055505050505050565b604051630429dd8560e41b8152600260048083019190915260248201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660448301527f00000000000000000000000000000000000000000000000000000000000000001660648201526084810183905281151560a4820152737b8bcf00def58b50620b2c253f3a97ee51f446839063429dd8509060c4015b60006040518083038186803b15801561151157600080fd5b505af4158015611525573d6000803e3d6000fd5b505050505050565b6000546001600160a01b0316331461155857604051634755657960e01b815260040160405180910390fd5b6040516356b16f7360e01b8152600560048201526024810183905260448101829052737b8bcf00def58b50620b2c253f3a97ee51f44683906356b16f73906064016114f9565b6000546001600160a01b031633146115c957604051634755657960e01b815260040160405180910390fd5b604051631de341c560e31b8152600360048201526001600160a01b0382166024820152737b8bcf00def58b50620b2c253f3a97ee51f446839063ef1a0e28906044015b60006040518083038186803b15801561162457600080fd5b505af4158015611638573d6000803e3d6000fd5b5050505050565b6040516331a9108f60e11b8152600481018290526000906116f1906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690636352211e90602401602060405180830381865afa1580156116ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116cf91906158c5565b6000848152600460209081526040808320600284529091529020548490612f68565b90506116fc81613070565b6000611707826130b0565b905060006117148461342f565b60c084015160608086015160408051858152602081018a905280820188905292830193909352608082015290519192507fb299642ff40e6ba13eec0f77203c3ef9816a9f64212a40467d63db0eba259b4f919081900360a00190a150505050565b6000546001600160a01b031633146117a057604051634755657960e01b815260040160405180910390fd5b604051635a79013160e11b8152600360048201526001600160a01b038416602482015260ff8316604482015260648101829052737b8bcf00def58b50620b2c253f3a97ee51f446839063b4f20262906084015b60006040518083038186803b15801561180b57600080fd5b505af4158015610b22573d6000803e3d6000fd5b6000546001600160a01b0316331461184a57604051634755657960e01b815260040160405180910390fd5b604051631428438f60e11b8152600260048201526001600160a01b0382166024820152737b8bcf00def58b50620b2c253f3a97ee51f4468390632850871e9060440161160c565b6118f06040518061014001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040516393760d3d60e01b81526003600480830191909152602482015260448101839052737b8bcf00def58b50620b2c253f3a97ee51f44683906393760d3d9060640161014060405180830381865af4158015611951573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061097491906159ac565b6040516332f6692360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f0000000000000000000000000000000000000000000000000000000000000000811660248301527f000000000000000000000000000000000000000000000000000000000000000016604482015233606482015260848101829052737b8bcf00def58b50620b2c253f3a97ee51f44683906332f669239060a40161160c565b6000546001600160a01b03163314611a5d57604051634755657960e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b0383169081179091556040519081527f71614071b88dee5e0b2ae578a9dd7b2ebbe9ae832ba419dc0242cd065a290b6c906020015b60405180910390a150565b600154600160a01b900460ff1615611add576040516325dbe6e160e21b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b179055604051633db1a4d360e01b8152737b8bcf00def58b50620b2c253f3a97ee51f4468390633db1a4d390611b4e908590859060c0907f000000000000000000000000000000000000000000000000000000000000000090600401615819565b60006040518083038186803b158015611b6657600080fd5b505af4158015611b7a573d6000803e3d6000fd5b50505050600082806020019051810190611b949190615a3a565b9050611ba88160200151826000015161377b565b611bbf8160200151826040015183606001516137f2565b6000611c167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636352211e84602001516040518263ffffffff1660e01b8152600401610c6691815260200190565b90506000611c358260c00151846080015161350390919063ffffffff16565b9050611c6681601260ff7f000000000000000000000000000000000000000000000000000000000000000016613508565b8260a001818151611c7791906158e2565b905250608083015160c083015260a083015160009015611cc257611c9a83613070565b611cbf838560400151600014611cb4578560a00151611cb8565b6000195b6000613877565b90505b611ccb836136d8565b611cd984602001513361450c565b8360a00151600003611d3e5783516020808601516040808801516060808a0151835196875294860193909352908401528201527f3b963caf777561b495590bdc6ec8abd33838524d0a3aafc3f6f1b7857449d0999060800160405180910390a1611dad565b83516020808601516040808801516060808a015189840151845197885295870194909452918501528301526001600160a01b0316608082015260a081018290527fc72ca5dc9c24b437f43b8c112e7b4e24b4a7bce4b1f405e591eee1cc32d327c29060c0015b60405180910390a15b50506001805460ff60a01b1916905550505050565b600084600003611e7e576040516335313c2160e11b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636a627842906024016020604051808303816000875af1158015611e32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e569190615902565b94508115611e79576000858152600460205260409020611e79906008600161459f565b611e88565b611e8885336145a8565b611e9184614668565b6000611e9e338787612f68565b90506000196001600160a01b03861601611ed35734841115611ed3576040516394613e9960e01b815260040160405180910390fd5b83600003611ef4576040516394613e9960e01b815260040160405180910390fd5b6001600160a01b038516600114611f1c578051611f1c906001600160a01b03871690866146ac565b611f268185614736565b611f2f816136d8565b6000611f3a8761342f565b60408051828152602081018a90526001600160a01b038916818301526060810188905290519192507f8eee26bd33caee967b96e711aa45ef94c734251b0457a0f2213fb392c01ef53a919081900360800190a186925050505b949350505050565b604051632a8bb6c960e11b815260026004828101919091526024820152604481018290526000908190737b8bcf00def58b50620b2c253f3a97ee51f44683906355176d92906064016040805180830381865af4158015611fff573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120239190615ab3565b91509150915091565b600154600160a01b900460ff1615612057576040516325dbe6e160e21b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b179055604051633db1a4d360e01b8152737b8bcf00def58b50620b2c253f3a97ee51f4468390633db1a4d3906120c8908590859060a0907f000000000000000000000000000000000000000000000000000000000000000090600401615819565b60006040518083038186803b1580156120e057600080fd5b505af41580156120f4573d6000803e3d6000fd5b5050505060008280602001905181019061210e9190615ad7565b90506121228160200151826000015161377b565b60006121797f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636352211e84602001516040518263ffffffff1660e01b8152600401610c6691815260200190565b905060006121988260c00151846060015161350390919063ffffffff16565b90506121c981601260ff7f000000000000000000000000000000000000000000000000000000000000000016613508565b8260a0018181516121da91906158e2565b905250606083015160c08301526121f082613070565b60006122028385608001516001613877565b90508360400151612212846130b0565b1015612231576040516341c092a960e01b815260040160405180910390fd5b61223a836136d8565b61224884602001513361450c565b83516020808601516040808701518151948552928401919091526001600160a01b0390911690820152606081018290527f39f415ac8ef6b49b1ba40c4fafb2b911df1c5306c49eef15c4bc38a0128fca9890608001611da4565b6000546001600160a01b031633146122cd57604051634755657960e01b815260040160405180910390fd5b6040516330af10eb60e21b81526002600482015260248101829052737b8bcf00def58b50620b2c253f3a97ee51f446839063c2bc43ac9060440161160c565b826000036123a5576040516335313c2160e11b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636a627842906024016020604051808303816000875af115801561237a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061239e9190615902565b92506123af565b6123af8333612d80565b6123b882614668565b60006123c5338585612f68565b6001600090815260056020527f1471eb6eb2c5e789fc3de43f8ce62938c7d1836ec861730447e2ada8fd81017b5491925090612402908690612e44565b90506000196001600160a01b0385160161241a578092505b8260000361243b576040516394613e9960e01b815260040160405180910390fd5b6001600160a01b038416600114612463578151612463906001600160a01b03861690856146ac565b61246d8284614736565b61247682613070565b6000612481836130b0565b905061248c836136d8565b60006124978761342f565b60c08086015160608088015160408051868152602081018e9052908101889052918201929092526080810191909152600060a08201529192507f0be79565c9c0f7b4144002bd9abeb6bd0e8f43023abae7066992c58bdff9397e9101610a8d565b6000546001600160a01b0316331461252357604051634755657960e01b815260040160405180910390fd5b604051633c5d969360e01b8152600360048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660248301527f0000000000000000000000000000000000000000000000000000000000000000811660448301527f0000000000000000000000000000000000000000000000000000000000000000811660648301527f00000000000000000000000000000000000000000000000000000000000000008116608483015280861660a4830152841660c482015260e481018390526101048101829052737b8bcf00def58b50620b2c253f3a97ee51f4468390633c5d9693906101240160006040518083038186803b15801561263557600080fd5b505af4158015612649573d6000803e3d6000fd5b5050505050505050565b6000546001600160a01b0316331461267e57604051634755657960e01b815260040160405180910390fd5b60408051633db1a4d360e01b8152737b8bcf00def58b50620b2c253f3a97ee51f4468391633db1a4d3916126db9186918691907f000000000000000000000000000000000000000000000000000000000000000090600401615819565b60006040518083038186803b1580156126f357600080fd5b505af4158015612707573d6000803e3d6000fd5b505050506000828060200190518101906127219190615b46565b8051909150461461273157600080fd5b602081015160405163ac46ae5160e01b8152600260048201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660248301527f0000000000000000000000000000000000000000000000000000000000000000811660448301527f00000000000000000000000000000000000000000000000000000000000000001660648201526084810191909152737b8bcf00def58b50620b2c253f3a97ee51f446839063ac46ae519060a4016117f3565b6000546001600160a01b0316331461282057604051634755657960e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f6b70829fcbe4891157f7a7496f9870927de3c8237adbe9cd39bae09b7382c40990602001611aa7565b6128b660405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b604051637d4b09b360e01b815260026004820152737b8bcf00def58b50620b2c253f3a97ee51f4468390637d4b09b39060240161010060405180830381865af4158015612907573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061292b9190615b94565b905090565b61293a86336145a8565b600560008190526020527f458b30c2d72bfd2c6317304a4594ecbafe5f729d3111b65fdc3a33bd48e5432d54612971908790612e44565b5083600003612993576040516394613e9960e01b815260040160405180910390fd5b60006129a0338888612f68565b90506129ab81613070565b60006129b6826130b0565b905060006129c483886131f7565b90506129d16064836157a2565b81116129db575060005b60006129e68a61342f565b90507ecae5d4a40f5ab841867a076a796230392d8ce403d409f84f18377b49a602d5818b848760c0015188606001518d8d8d8d604051612a2e99989796959493929190615c3a565b60405180910390a150505050505050505050565b604051630afc7a3960e31b815260056004820152606090737b8bcf00def58b50620b2c253f3a97ee51f44683906357e3d1c890602401600060405180830381865af4158015612a95573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261292b9190810190615c89565b612ac783336145a8565b600360005260056020527fa9bc9a3a348c357ba16b37005d7e6b3236198c0e939f4af8c5f19b8deeb8ebc054612afe908490612e44565b5080600003612b20576040516394613e9960e01b815260040160405180910390fd5b6000612b2d338585612f68565b9050612b3881613070565b6000612b43826130b0565b90506000612b5183856131f7565b9050612b5e6064836157a2565b8111612b68575060005b6000612b738761342f565b60c08086015160608088015160408051868152602081018e905290810188905291820192909252608081019190915260a081018890529192507f88ad2ade0e436c3cffc2b0d1b51fb1379e5bca5dd3aaef801b234b18aa87d2a99101610a8d565b612c276040518061010001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160001515815260200160008152602001600081525090565b60405163ee55e7b560e01b81526003600480830191909152602482015260448101839052737b8bcf00def58b50620b2c253f3a97ee51f446839063ee55e7b59060640161010060405180830381865af4158015612c88573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109749190615d22565b612cb684336145a8565b600460005260056020527f3eec716f11ba9e820c81ca75eb978ffb45831ef8b7a53e5e422c26008e1ca6d554612ced908590612e44565b50600084815260046020908152604080832060028452909152812054612d169033908790612f68565b9050612d2181613070565b6000612d2c826130b0565b90506000612d398761342f565b90507fd8eb4847ae2a642c0c24f4336eb3ac86a4cac889da47bb554f95c4d6163d80ff8188848660c0015187606001518b8b8b604051610a8d989796959493929190615da4565b806001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636352211e846040518263ffffffff1660e01b8152600401612dd891815260200190565b602060405180830381865afa158015612df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e1991906158c5565b6001600160a01b031614612e405760405163a03dca6f60e01b815260040160405180910390fd5b5050565b6006600090815260026020527f59dd4b18488d12f51eda69757a0ed42a2010c14b564330cc74a06895e60c077b5482341015612e9357604051639413602360e01b815260040160405180910390fd5b6000612e9f8285615999565b6007600090815260026020527facd8ef244210bb6898e73c48bf820ed8ecc857a3bab8d79c10e4fa92b1e9ca655491925090612edc9083906157b6565b9050612eeb6002600783613760565b60008681526004602090815260408083206009845290915280822054600a8352912054612f1882826157b6565b6000898152600460205260409020859350909150612f3890600984613760565b6000888152600460205260409020612f5290600a83613760565b612f5c8734615999565b98975050505050505050565b612fd260405180610120016040528060006001600160a01b031681526020016000815260200160006001600160a01b031681526020016000815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b038481168252602080830185905283821660408085018290527fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e05460608601526000918252600380845281832060018452845281832054909416608086015286825260048084528183209483528484528183205460a0870152825292909152205460c08201526130698383614997565b9392505050565b6040818101516001600160a01b031660009081526003602081815283832091835252205460e082015260408101516130a790614a95565b61010090910152565b600080670de0b6b3a76400008360e00151670de0b6b3a764000085610100015186608001516001600160a01b0316631e01043988602001516040518263ffffffff1660e01b815260040161310691815260200190565b602060405180830381865afa158015613123573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131479190615902565b6131519190615dea565b61315b91906157a2565b6131659190615dea565b61316f91906157a2565b90506000808460a00151121561319a576131958460a0015161319090615e01565b61358f565b61319d565b60005b90508082106131f0576131ed7f000000000000000000000000000000000000000000000000000000000000000060ff1660126131e68760a0015186614c1390919063ffffffff16565b9190614c40565b92505b5050919050565b600082610100015160001961320c91906157a2565b8210156109745760808301516020840151604051631e01043960e01b815260048101919091526000916001600160a01b031690631e01043990602401602060405180830381865afa158015613265573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132899190615902565b90508083106133295760008460a001511315613324576000670de0b6b3a764000085610100015183866132bc9190615999565b6132c69190615dea565b6132d091906157a2565b905060006132e18660a0015161358f565b9050818111156133215761331e60ff7f00000000000000000000000000000000000000000000000000000000000000001660126131e68585615999565b93505b50505b613428565b6000670de0b6b3a76400008560e00151670de0b6b3a764000087610100015187866133549190615999565b61335e9190615dea565b61336891906157a2565b6133729190615dea565b61337c91906157a2565b905060008560a00151126133d1576133ca7f000000000000000000000000000000000000000000000000000000000000000060ff1660126131e68860a0015185614c1390919063ffffffff16565b9250613426565b60006133e48660a0015161319090615e01565b9050808211156134245761342160ff7f00000000000000000000000000000000000000000000000000000000000000001660126131e68486615999565b93505b505b505b5092915050565b6005600090815260026020527fb98b78633099fa36ed8b8680c4f8092689e1e04080eb9cbb077ca38a14d7e38454613468600182615e1d565b9050613480600260056001600160801b038416613760565b60008381526004602090815260408083206001845290915281205490506134a8600182615e1d565b60008581526004602052604090209091506134ce9060016001600160801b038416613760565b60006134fa6001600160801b0383166fffffffffffffffffffffffffffffffff19608086901b166157b6565b95945050505050565b900390565b600080613516858585613698565b905060008512801561353257508461352f828587613698565b14155b15611f93576134fa60018261591b565b60006001600160ff1b0382111561357457604051632a26bd1360e01b8152600481018390526024015b60405180910390fd5b5090565b6000818313156135885781613069565b5090919050565b60008082121561357457604051632a33bb3160e01b81526004810183905260240161356b565b60006135c084614c73565b90506000196001600160a01b0385160161364e576000836001600160a01b03168360405160006040518083038185875af1925050503d8060008114613621576040519150601f19603f3d011682016040523d82523d6000602084013e613626565b606091505b50509050806136485760405163e277d13760e01b815260040160405180910390fd5b50613662565b6136626001600160a01b0385168484614cfa565b600061366d85614c73565b905061367983826157b6565b82146116385760405163f4d4667360e01b815260040160405180910390fd5b60008183146136d0576136ac83600a615f21565b6136b783600a615f21565b6136c1908661593b565b6136cb919061596b565b611f93565b509192915050565b60608101516136ec90600290600190613760565b604080820151602080840151600090815260049091529190912061371291600290614d5d565b60a0810151602080830151600090815260049091526040902061373791600390613760565b60c081015160208083015160009081526004918290526040902061375d92909190613760565b50565b805b60ff909216600090815260209390935250604090912055565b60008281526004602090815260408083206001845290915290205481906001600160801b038216146137c0576040516302e8145360e61b815260040160405180910390fd5b6137ed60016137cf8382615e1d565b600086815260046020526040902091906001600160801b0316613760565b505050565b6000806137fe85611f9b565b909250905061380f60028042613760565b61381c6002600385613760565b6138296002600484613760565b600085815260046020526040902061384390600586613760565b600085815260046020526040902061385d90600683613760565b600085815260046020526040902061163890600784613760565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c19d93fb6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156138d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138fb9190615f2d565b1561390557600080fd5b600061393260027f0000000000000000000000000000000000000000000000000000000000000000615f4a565b61393d90600a615f63565b8492509050613956670de0b6b3a76400006000196157a2565b82108015613968575060008560a00151125b15613a24577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031685604001516001600160a01b0316036139cc576139bb8560a0015161319090615e01565b6139c590836157b6565b9150613a24565b6064856101000151670de0b6b3a76400006139ee8860a0015161319090615e01565b6139f89190615dea565b613a0291906157a2565b613a0d906069615dea565b613a1791906157a2565b613a2190836157b6565b91505b60808501516020860151604051637cbc237360e01b81526001600160a01b0390921691637cbc237391613a64918690600401918252602082015260400190565b6020604051808303816000875af1158015613a83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613aa79190615902565b9150600080600086851115613cb7576000613ac28887615999565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168a604001516001600160a01b031603613b16575080613b0f8188615999565b9650613c8b565b60016001600160a01b03168a604001516001600160a01b031603613bd6576000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f29216e2856040518263ffffffff1660e01b8152600401604080518083038185885af1158015613b96573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613bbb9190615ab3565b9093508392509050613bcd818a615999565b98505050613c8b565b60408a8101519051633e87f49360e01b81526001600160a01b0391821660048201526024810184905260009182917f000000000000000000000000000000000000000000000000000000000000000090911690633e87f4939060440160408051808303816000875af1158015613c50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c749190615ab3565b9093508392509050613c86818a615999565b985050505b613c9581866157b6565b9450613ca081613542565b8a60a001818151613cb191906158e2565b90525050505b600085118015613ccb575060008860a00151125b15613f08576000613ce38960a0015161319090615e01565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168a604001516001600160a01b031603613d4757818710613d3e575080613d378188615999565b9650613edc565b50600095613edc565b85821015613d53578591505b60016001600160a01b03168a604001516001600160a01b031603613e20576000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316637661f4f68a866040518363ffffffff1660e01b8152600401613dc391815260200190565b604080518083038185885af1158015613de0573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613e059190615ab3565b9093508392509050613e17818a615999565b98505050613edc565b60408a810151905163bf441a5960e01b81526001600160a01b039182166004820152602481018490526044810189905260009182917f00000000000000000000000000000000000000000000000000000000000000009091169063bf441a599060640160408051808303816000875af1158015613ea1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ec59190615ab3565b9093508392509050613ed7818a615999565b985050505b613ee681866157b6565b9450613ef181613542565b8a60a001818151613f0291906158e2565b90525050505b60008860a001511315614116576000613f2b670de0b6b3a76400006000196157a2565b8810613f4557613f3e8960a0015161358f565b9050613fb0565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031689604001516001600160a01b0316148015613f8957508786105b15613fb057613fad613f9e8a60a0015161358f565b613fa8888b615999565b614d6c565b90505b8015614114576000848211156140c75760006001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016637cbc237382613ffc8987615999565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af115801561403f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140639190615902565b905061406f8684615999565b8110156140b157881561409857806140878785615999565b6140919190615999565b93506140b1565b6040516320b7831f60e11b815260040160405180910390fd5b6140bb81876157b6565b915060009550506140d6565b50806140d38186615999565b94505b6140e081856157b6565b93506140eb83613542565b6140f482613542565b6140fe91906158e2565b8a60a00181815161410f919061591b565b905250505b505b82156141b057604051631c57762b60e31b815260006004820152602481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e2bbb158906044016020604051808303816000875af115801561418a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141ae9190615902565b505b8115614451578515614246577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031688604001516001600160a01b03160361420a5761420382866157b6565b9450614451565b8751614241906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690846135b5565b614451565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031688604001516001600160a01b03160361428d5761420382866157b6565b838210156142e4576142da6142d17f000000000000000000000000000000000000000000000000000000000000000060ff1660126142ca86613542565b9190613698565b60608a01510190565b6060890152614451565b60016001600160a01b031688604001516001600160a01b0316036143a557604051632cb4d9d960e11b8152600481018390526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690635969b3b29060240160408051808303816000875af115801561436b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061438f9190615ab3565b915061439d905081876157b6565b955050614451565b604088810151905163045608af60e01b81526001600160a01b039182166004820152602481018490526000917f0000000000000000000000000000000000000000000000000000000000000000169063045608af9060440160408051808303816000875af115801561441b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061443f9190615ab3565b915061444d905081876157b6565b9550505b84156144745787516040890151614474916001600160a01b0390911690876135b5565b80156145015787516040516340c10f1960e01b81526001600160a01b039182166004820152602481018390527f0000000000000000000000000000000000000000000000000000000000000000909116906340c10f1990604401600060405180830381600087803b1580156144e857600080fd5b505af11580156144fc573d6000803e3d6000fd5b505050505b505050509392505050565b60008281526004602090815260408083206009845290915290205480156137ed57600760005260026020527facd8ef244210bb6898e73c48bf820ed8ecc857a3bab8d79c10e4fa92b1e9ca65546145638282615999565b90506145726002600783613760565b600084815260046020908152604080832060098452909152812055614599600184846135b5565b50505050565b61376281614d7c565b806001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636352211e846040518263ffffffff1660e01b815260040161460091815260200190565b602060405180830381865afa15801561461d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061464191906158c5565b6001600160a01b031614612e4057604051639289a15b60e01b815260040160405180910390fd5b6001600160a01b038181166000908152600360209081526040808320600184529091529020541661375d57604051634033aec960e01b815260040160405180910390fd5b6000196001600160a01b038416016146de573481146137ed57604051630fc9bd0f60e11b815260040160405180910390fd5b60006146e984614c73565b90506147006001600160a01b038516843085614d96565b600061470b85614c73565b905061471783836157b6565b811461163857604051630fc9bd0f60e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031682604001516001600160a01b031603614873576000670de0b6b3a76400006147a87f000000000000000000000000000000000000000000000000000000000000000084615dea565b6147b291906157a2565b90506147be8183615999565b604051631c57762b60e31b815260006004820152602481018390529092507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063e2bbb158906044016020604051808303816000875af115801561482f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148539190615902565b5061485d81613542565b8360a00181815161486e91906158e2565b905250505b60016001600160a01b031682604001516001600160a01b0316036149145781608001516001600160a01b031663e2bbb158828460200151846040518463ffffffff1660e01b81526004016148d1929190918252602082015260400190565b60206040518083038185885af11580156148ef573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906137ed9190615902565b60808201516020830151604051631c57762b60e31b81526001600160a01b039092169163e2bbb15891614954918590600401918252602082015260400190565b6020604051808303816000875af1158015614973573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137ed9190615902565b6000828152600460209081526040808320600284529091529020546001600160a01b038116158015906149dc5750816001600160a01b0316816001600160a01b031614155b156137ed576001600160a01b0381166000908152600360209081526040808320600184529091528120546001600160a01b0316635d988967856040518263ffffffff1660e01b8152600401614a3391815260200190565b602060405180830381865afa158015614a50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a749190615902565b9050801561459957604051634033aec960e01b815260040160405180910390fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031603614adf5750670de0b6b3a7640000919050565b6000614af3836001600160a01b0316614dce565b9050614be98160ff167f000000000000000000000000000000000000000000000000000000000000000060ff166131e67f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166369843940614b8a6002600360008c6001600160a01b03166001600160a01b03168152602001908152602001600020614e4b90919063ffffffff16565b6040518263ffffffff1660e01b8152600401614ba891815260200190565b602060405180830381865afa158015614bc5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131909190615902565b915081600003614c0c5760405163089112d560e21b815260040160405180910390fd5b505b919050565b6000808212614c2d57614c2682846157b6565b9050610974565b614c3682615e01565b614c269084615999565b60008183146136d057614c5483600a615f21565b614c5f83600a615f21565b614c699086615dea565b6136cb91906157a2565b60006001600160a01b038216600114614cf3576040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015614cca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614cee9190615902565b610974565b4792915050565b6040516001600160a01b0383166024820152604481018290526137ed90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614e61565b6001600160a01b038116613762565b6000818311156135885781613069565b600081614d8a576000614d8d565b60015b60ff1692915050565b6040516001600160a01b03808516602483015283166044820152606481018290526145999085906323b872dd60e01b90608401614d26565b60006001600160a01b038216600114614e4357816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015614e1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614cee9190615f72565b601292915050565b60ff166000908152602091909152604090205490565b6000614eb6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614f369092919063ffffffff16565b9050805160001480614ed7575080806020019051810190614ed79190615f2d565b6137ed5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161356b565b6060611f93848460008585600080866001600160a01b03168587604051614f5d9190615f8f565b60006040518083038185875af1925050503d8060008114614f9a576040519150601f19603f3d011682016040523d82523d6000602084013e614f9f565b606091505b5091509150614fb087838387614fbb565b979650505050505050565b6060831561502a578251600003615023576001600160a01b0385163b6150235760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161356b565b5081611f93565b611f93838381511561503f5781518083602001fd5b8060405162461bcd60e51b815260040161356b9190615fab565b6001600160a01b038116811461375d57600080fd5b60006020828403121561508057600080fd5b813561306981615059565b6000806000606084860312156150a057600080fd5b8335925060208401356150b281615059565b929592945050506040919091013590565b60008083601f8401126150d557600080fd5b5081356001600160401b038111156150ec57600080fd5b6020830191508360208260051b850101111561510757600080fd5b9250929050565b801515811461375d57600080fd5b600080600080600080600060c0888a03121561513757600080fd5b87359650602088013561514981615059565b9550604088013594506060880135935060808801356001600160401b0381111561517257600080fd5b61517e8a828b016150c3565b90945092505060a08801356151928161510e565b8091505092959891949750929550565b634e487b7160e01b600052604160045260246000fd5b60405161014081016001600160401b03811182821017156151db576151db6151a2565b60405290565b60405161010081016001600160401b03811182821017156151db576151db6151a2565b604051601f8201601f191681016001600160401b038111828210171561522c5761522c6151a2565b604052919050565b600082601f83011261524557600080fd5b81356001600160401b0381111561525e5761525e6151a2565b615271601f8201601f1916602001615204565b81815284602083860101111561528657600080fd5b816020850160208301376000918101602001919091529392505050565b600080604083850312156152b657600080fd5b82356001600160401b03808211156152cd57600080fd5b6152d986838701615234565b935060208501359150808211156152ef57600080fd5b506152fc85828601615234565b9150509250929050565b6000806040838503121561531957600080fd5b82359150602083013561532b8161510e565b809150509250929050565b6000806040838503121561534957600080fd5b50508035926020909101359150565b60006020828403121561536a57600080fd5b5035919050565b60ff8116811461375d57600080fd5b60008060006060848603121561539557600080fd5b83356153a081615059565b925060208401356150b281615371565b815181526020808301516101408301916153d4908401826001600160a01b03169052565b5060408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151818401525092915050565b6000806000806080858703121561544557600080fd5b84359350602085013561545781615059565b925060408501359150606085013561546e8161510e565b939692955090935050565b6000806000806080858703121561548f57600080fd5b843561549a81615059565b935060208501356154aa81615059565b93969395505050506040820135916060013590565b81516001600160a01b031681526101e0810160208301516154eb60208401826001600160a01b03169052565b50604083015161550660408401826001600160a01b03169052565b50606083015161552160608401826001600160a01b03169052565b50608083015161553c60808401826001600160a01b03169052565b5060a083015161555760a08401826001600160a01b03169052565b5060c083015161557260c08401826001600160a01b03169052565b5060e083015161558d60e08401826001600160a01b03169052565b506101008381015190830152610120808401519083015261014080840151908301526101608084015190830152610180808401516001600160a01b0381168285015250506101a0838101516001600160a01b0381168483015250506101c0838101516001600160a01b03811684830152613426565b60008060008060008060a0878903121561561b57600080fd5b86359550602087013561562d81615059565b9450604087013593506060870135925060808701356001600160401b0381111561565657600080fd5b61566289828a016150c3565b979a9699509497509295939492505050565b6020808252825182820181905260009190848201906040850190845b818110156156ac57835183529284019291840191600101615690565b50909695505050505050565b600080600080606085870312156156ce57600080fd5b843593506020850135925060408501356001600160401b038111156156f257600080fd5b6156fe878288016150c3565b95989497509550505050565b8051614c0e81615059565b60006060828403121561572757600080fd5b604051606081018181106001600160401b0382111715615749576157496151a2565b604052825161575781615059565b8152602083810151908201526040928301519281019290925250919050565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000826157b1576157b1615776565b500490565b808201808211156109745761097461578c565b60005b838110156157e45781810151838201526020016157cc565b50506000910152565b600081518084526158058160208601602086016157c9565b601f01601f19169290920160200192915050565b60808152600061582c60808301876157ed565b828103602084015261583e81876157ed565b604084019590955250506001600160a01b039190911660609091015292915050565b60006080828403121561587257600080fd5b604051608081018181106001600160401b0382111715615894576158946151a2565b8060405250825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b6000602082840312156158d757600080fd5b815161306981615059565b80820182811260008312801582168215821617156134265761342661578c565b60006020828403121561591457600080fd5b5051919050565b81810360008312801583831316838312821617156134285761342861578c565b80820260008212600160ff1b841416156159575761595761578c565b81810583148215176109745761097461578c565b60008261597a5761597a615776565b600160ff1b8214600019841416156159945761599461578c565b500590565b818103818111156109745761097461578c565b600061014082840312156159bf57600080fd5b6159c76151b8565b825181526159d76020840161570a565b602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152506101208084015181830152508091505092915050565b600060c08284031215615a4c57600080fd5b60405160c081018181106001600160401b0382111715615a6e57615a6e6151a2565b8060405250825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a08201528091505092915050565b60008060408385031215615ac657600080fd5b505080516020909101519092909150565b600060a08284031215615ae957600080fd5b60405160a081018181106001600160401b0382111715615b0b57615b0b6151a2565b806040525082518152602083015160208201526040830151604082015260608301516060820152608083015160808201528091505092915050565b600060408284031215615b5857600080fd5b604051604081018181106001600160401b0382111715615b7a57615b7a6151a2565b604052825181526020928301519281019290925250919050565b60006101008284031215615ba757600080fd5b615baf6151e1565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201528091505092915050565b8183526000602080850194508260005b85811015615c2f57813587529582019590820190600101615c13565b509495945050505050565b60006101008b83528a60208401528960408401528860608401528760808401528660a08401528560c08401528060e0840152615c798184018587615c03565b9c9b505050505050505050505050565b60006020808385031215615c9c57600080fd5b82516001600160401b0380821115615cb357600080fd5b818501915085601f830112615cc757600080fd5b815181811115615cd957615cd96151a2565b8060051b9150615cea848301615204565b8181529183018401918481019088841115615d0457600080fd5b938501935b83851015612f5c57845182529385019390850190615d09565b60006101008284031215615d3557600080fd5b615d3d6151e1565b825181526020830151615d4f81615059565b8060208301525060408301516040820152606083015160608201526080830151608082015260a0830151615d828161510e565b60a082015260c0838101519082015260e0928301519281019290925250919050565b8881528760208201528660408201528560608201528460808201528360a082015260e060c08201526000615ddc60e083018486615c03565b9a9950505050505050505050565b80820281158282048414176109745761097461578c565b6000600160ff1b8201615e1657615e1661578c565b5060000390565b6001600160801b038181168382160190808211156134285761342861578c565b600181815b80851115615e78578160001904821115615e5e57615e5e61578c565b80851615615e6b57918102915b93841c9390800290615e42565b509250929050565b600082615e8f57506001610974565b81615e9c57506000610974565b8160018114615eb25760028114615ebc57615ed8565b6001915050610974565b60ff841115615ecd57615ecd61578c565b50506001821b610974565b5060208310610133831016604e8410600b8410161715615efb575081810a610974565b615f058383615e3d565b8060001904821115615f1957615f1961578c565b029392505050565b60006130698383615e80565b600060208284031215615f3f57600080fd5b81516130698161510e565b60ff82811682821603908111156109745761097461578c565b600061306960ff841683615e80565b600060208284031215615f8457600080fd5b815161306981615371565b60008251615fa18184602087016157c9565b9190910192915050565b60208152600061306960208301846157ed56fea26469706673582212202b223b63ea2397fb0b32e8c02881d93adcd67c430dd2ba9f7ec10e13cc0847cd64736f6c6343000814003300000000000000000000000018f2cba9254d0b8854180cacad7b0567b39f44c600000000000000000000000055746ac5c15be05b43d7392f0e3867e86f573a66000000000000000000000000bca4439e99091afb297ecb4c5672357e467664f2000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f42900000000000000000000000093e6e4d43f34c1bf9385ecbb0fce912537ecf56a000000000000000000000000b5284ed1606b91e0129182d55ee7ee31c31c920c00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea00000000000000000000000000000000000000000000000002c68af0bb14000000000000000000000000000000000000000000000000000006f05b59d3b200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001dcd650000000000000000000000000042dd5168eda433d9c7523ad11ea15264343b186200000000000000000000000051393080c546623d04ccacf86428664856c7ac76000000000000000000000000740e33ea864a1ee3d6c0a76f281ba1940859bf62
Deployed Bytecode
0x6080604052600436106101d85760003560e01c80637e9459ba11610102578063d784d42611610095578063f7c60d4411610064578063f7c60d441461080b578063f802a6971461081e578063f851a440146108a5578063fa321c26146108c557600080fd5b8063d784d42614610740578063db40156614610760578063e1faff56146107d6578063e926bc9d146107e957600080fd5b8063aaba0398116100d1578063aaba039814610490578063ab1aac60146104a3578063c323acb6146104c3578063cd18dde31461072057600080fd5b80637e9459ba146103fa5780638769da2d1461041b5780639b1a2e9414610450578063a6445bb51461047057600080fd5b80633e0c7fc11161017a578063619cf2be11610149578063619cf2be1461036d578063702e6c061461039a578063704b6c02146103ba57806370a1848c146103da57600080fd5b80633e0c7fc1146102d557806349006539146102f55780635c60da1b146103155780635c8e13a61461034d57600080fd5b80631c58d9c1116101b65780631c58d9c11461025557806323cc0ebf1461027557806327a029141461029557806338f438a3146102b557600080fd5b8063068689cd146101dd5780630c8aeb561461022d57806316890fc214610242575b600080fd5b3480156101e957600080fd5b506101fd6101f836600461506e565b6108d8565b6040805182516001600160a01b031681526020808401519082015291810151908201526060015b60405180910390f35b61024061023b36600461508b565b61097a565b005b61024061025036600461511c565b610a9e565b34801561026157600080fd5b506102406102703660046152a3565b610b2b565b34801561028157600080fd5b50610240610290366004615306565b611456565b3480156102a157600080fd5b506102406102b0366004615336565b61152d565b3480156102c157600080fd5b506102406102d036600461506e565b61159e565b3480156102e157600080fd5b506102406102f0366004615358565b61163f565b34801561030157600080fd5b50610240610310366004615380565b611775565b34801561032157600080fd5b50600154610335906001600160a01b031681565b6040516001600160a01b039091168152602001610224565b34801561035957600080fd5b5061024061036836600461506e565b61181f565b34801561037957600080fd5b5061038d610388366004615358565b611891565b60405161022491906153b0565b3480156103a657600080fd5b506102406103b5366004615358565b611975565b3480156103c657600080fd5b506102406103d536600461506e565b611a32565b3480156103e657600080fd5b506102406103f53660046152a3565b611ab2565b61040d61040836600461542f565b611dc2565b604051908152602001610224565b34801561042757600080fd5b5061043b610436366004615358565b611f9b565b60408051928352602083019190915201610224565b34801561045c57600080fd5b5061024061046b3660046152a3565b61202c565b34801561047c57600080fd5b5061024061048b366004615358565b6122a2565b61024061049e36600461508b565b61230c565b3480156104af57600080fd5b506102406104be366004615479565b6124f8565b3480156104cf57600080fd5b50604080516101e08101825260006101c08201526001600160a01b037f00000000000000000000000018f2cba9254d0b8854180cacad7b0567b39f44c6811682527f00000000000000000000000055746ac5c15be05b43d7392f0e3867e86f573a66811660208301527f000000000000000000000000bca4439e99091afb297ecb4c5672357e467664f28116828401527f000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f429811660608301527f00000000000000000000000093e6e4d43f34c1bf9385ecbb0fce912537ecf56a811660808301527f000000000000000000000000b5284ed1606b91e0129182d55ee7ee31c31c920c811660a08301527f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b811660c08301527f000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea811660e08301527f00000000000000000000000000000000000000000000000002c68af0bb1400006101008301527f00000000000000000000000000000000000000000000000006f05b59d3b200006101208301527f00000000000000000000000000000000000000000000000000000000000000006101408301527f000000000000000000000000000000000000000000000000000000001dcd65006101608301527f00000000000000000000000042dd5168eda433d9c7523ad11ea15264343b186281166101808301527f00000000000000000000000051393080c546623d04ccacf86428664856c7ac76166101a0820152905161022491906154bf565b34801561072c57600080fd5b5061024061073b3660046152a3565b612653565b34801561074c57600080fd5b5061024061075b36600461506e565b6127f5565b34801561076c57600080fd5b5061077561286e565b6040516102249190600061010082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b6102406107e4366004615602565b612930565b3480156107f557600080fd5b506107fe612a42565b6040516102249190615674565b61024061081936600461508b565b612abd565b34801561082a57600080fd5b5061083e610839366004615358565b612bd4565b6040516102249190815181526020808301516001600160a01b03169082015260408083015190820152606080830151908201526080808301519082015260a08083015115159082015260c0808301519082015260e091820151918101919091526101000190565b3480156108b157600080fd5b50600054610335906001600160a01b031681565b6102406108d33660046156b8565b612cac565b604080516060810182526000808252602082018190528183015290516340d24f1f60e11b8152600360048201526001600160a01b0383166024820152737b8bcf00def58b50620b2c253f3a97ee51f44683906381a49e3e90604401606060405180830381865af4158015610950573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109749190615715565b92915050565b6109848333612d80565b600260005260056020527f89832631fb3c3307a103ba2c84ab569c64d6182a18893dcd163f0f1c2090733a546109bb908490612e44565b50806000036109dd576040516394613e9960e01b815260040160405180910390fd5b60006109ea338585612f68565b90506109f581613070565b6000610a00826130b0565b90506000610a0e83856131f7565b9050610a1b6064836157a2565b8111610a25575060005b6000610a308761342f565b60c08086015160608088015160408051868152602081018e905290810188905291820192909252608081019190915260a081018890529192507f0be79565c9c0f7b4144002bd9abeb6bd0e8f43023abae7066992c58bdff9397e91015b60405180910390a150505050505050565b6000196001600160a01b03871601610b0857600460005260056020527f3eec716f11ba9e820c81ca75eb978ffb45831ef8b7a53e5e422c26008e1ca6d55434610ae782886157b6565b1115610b06576040516394613e9960e01b815260040160405180910390fd5b505b610b1487878784611dc2565b9650610b2287858585612cac565b50505050505050565b600154600160a01b900460ff1615610b56576040516325dbe6e160e21b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b179055604051633db1a4d360e01b8152737b8bcf00def58b50620b2c253f3a97ee51f4468390633db1a4d390610bc790859085906080907f000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea90600401615819565b60006040518083038186803b158015610bdf57600080fd5b505af4158015610bf3573d6000803e3d6000fd5b50505050600082806020019051810190610c0d9190615860565b90506000610ccc7f00000000000000000000000055746ac5c15be05b43d7392f0e3867e86f573a666001600160a01b0316636352211e84602001516040518263ffffffff1660e01b8152600401610c6691815260200190565b602060405180830381865afa158015610c83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca791906158c5565b6020848101516000818152600483526040808220600283529093529190912054612f68565b90506000610ceb8260c00151846040015161350390919063ffffffff16565b9050610d1c81601260ff7f000000000000000000000000000000000000000000000000000000000000000616613508565b8260a001818151610d2d91906158e2565b90525060408381015160c0840152608083015160208401519151637cbc237360e01b81526004810192909252600019602483015260009182916001600160a01b031690637cbc2373906044016020604051808303816000875af1158015610d98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dbc9190615902565b90507f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b6001600160a01b031684604001516001600160a01b031603610e0c57610e0581836157b6565b9150610f6a565b604084810151602086015160a0870151606089015193516330706e4160e01b81527f000000000000000000000000000000000000000000000000000000000000000660ff1660048201526001600160a01b0393841660248201527f000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f429841660448201527f00000000000000000000000051393080c546623d04ccacf86428664856c7ac76841660648201527f00000000000000000000000055746ac5c15be05b43d7392f0e3867e86f573a66909316608484015260a483019190915260c482015260e48101839052610104810191909152737b8bcf00def58b50620b2c253f3a97ee51f44683906330706e419061012401602060405180830381865af4158015610f39573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5d9190615902565b610f6790836157b6565b91505b5060008360a00151610f7b83613542565b610f8591906158e2565b905060007f00000000000000000000000000000000000000000000000000000000000000008213610fd757507f0000000000000000000000000000000000000000000000000000000000000000611076565b6110737f0000000000000000000000000000000000000000000000000000000000000000670de0b6b3a76400007f00000000000000000000000000000000000000000000000006f05b59d3b2000061102f838761591b565b611039919061593b565b611043919061596b565b61104d91906158e2565b7f000000000000000000000000000000000000000000000000000000001dcd6500613578565b90505b60006110818261358f565b90508381116110cf576110be6001600160a01b037f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b1633836135b5565b6110c88185615999565b93506111cd565b60006001600160a01b037f00000000000000000000000093e6e4d43f34c1bf9385ecbb0fce912537ecf56a16637cbc23738261110b8886615999565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af115801561114e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111729190615902565b90506111b33361118283886157b6565b6001600160a01b037f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b1691906135b5565b6111c56111c082876157b6565b613542565b925060009450505b6111d7828461591b565b925050821561127457604051631c57762b60e31b815260006004820152602481018490527f00000000000000000000000093e6e4d43f34c1bf9385ecbb0fce912537ecf56a6001600160a01b03169063e2bbb158906044016020604051808303816000875af115801561124e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112729190615902565b505b6112af6112a68360ff7f0000000000000000000000000000000000000000000000000000000000000006166012613698565b60608701510190565b6060860152600060a08601526112c4856136d8565b6020868101805160009081526004808452604080832060098085529086528184205485518552838752828520600a8087529088528386205487518752858952848720938752928852838620869055955185529286528184209484529385528220829055600790915260029092527facd8ef244210bb6898e73c48bf820ed8ecc857a3bab8d79c10e4fa92b1e9ca655461135d82846157b6565b6113679082615999565b90506113766002600783613760565b5050506020860151604051630852cd8d60e31b815260048101919091527f00000000000000000000000055746ac5c15be05b43d7392f0e3867e86f573a666001600160a01b0316906342966c6890602401600060405180830381600087803b1580156113e157600080fd5b505af11580156113f5573d6000803e3d6000fd5b505087516020808a0151604080519384529183015281018590527f8c3b5c5c5dddce5cc5abfa2cc66ec5dd06f76f9e9bb21debb1a85ad88fdbdb6f9250606001905060405180910390a150506001805460ff60a01b19169055505050505050565b604051630429dd8560e41b8152600260048083019190915260248201526001600160a01b037f00000000000000000000000018f2cba9254d0b8854180cacad7b0567b39f44c6811660448301527f00000000000000000000000055746ac5c15be05b43d7392f0e3867e86f573a661660648201526084810183905281151560a4820152737b8bcf00def58b50620b2c253f3a97ee51f446839063429dd8509060c4015b60006040518083038186803b15801561151157600080fd5b505af4158015611525573d6000803e3d6000fd5b505050505050565b6000546001600160a01b0316331461155857604051634755657960e01b815260040160405180910390fd5b6040516356b16f7360e01b8152600560048201526024810183905260448101829052737b8bcf00def58b50620b2c253f3a97ee51f44683906356b16f73906064016114f9565b6000546001600160a01b031633146115c957604051634755657960e01b815260040160405180910390fd5b604051631de341c560e31b8152600360048201526001600160a01b0382166024820152737b8bcf00def58b50620b2c253f3a97ee51f446839063ef1a0e28906044015b60006040518083038186803b15801561162457600080fd5b505af4158015611638573d6000803e3d6000fd5b5050505050565b6040516331a9108f60e11b8152600481018290526000906116f1906001600160a01b037f00000000000000000000000055746ac5c15be05b43d7392f0e3867e86f573a661690636352211e90602401602060405180830381865afa1580156116ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116cf91906158c5565b6000848152600460209081526040808320600284529091529020548490612f68565b90506116fc81613070565b6000611707826130b0565b905060006117148461342f565b60c084015160608086015160408051858152602081018a905280820188905292830193909352608082015290519192507fb299642ff40e6ba13eec0f77203c3ef9816a9f64212a40467d63db0eba259b4f919081900360a00190a150505050565b6000546001600160a01b031633146117a057604051634755657960e01b815260040160405180910390fd5b604051635a79013160e11b8152600360048201526001600160a01b038416602482015260ff8316604482015260648101829052737b8bcf00def58b50620b2c253f3a97ee51f446839063b4f20262906084015b60006040518083038186803b15801561180b57600080fd5b505af4158015610b22573d6000803e3d6000fd5b6000546001600160a01b0316331461184a57604051634755657960e01b815260040160405180910390fd5b604051631428438f60e11b8152600260048201526001600160a01b0382166024820152737b8bcf00def58b50620b2c253f3a97ee51f4468390632850871e9060440161160c565b6118f06040518061014001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040516393760d3d60e01b81526003600480830191909152602482015260448101839052737b8bcf00def58b50620b2c253f3a97ee51f44683906393760d3d9060640161014060405180830381865af4158015611951573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061097491906159ac565b6040516332f6692360e01b81526001600160a01b037f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b811660048301527f00000000000000000000000093e6e4d43f34c1bf9385ecbb0fce912537ecf56a811660248301527f000000000000000000000000b5284ed1606b91e0129182d55ee7ee31c31c920c16604482015233606482015260848101829052737b8bcf00def58b50620b2c253f3a97ee51f44683906332f669239060a40161160c565b6000546001600160a01b03163314611a5d57604051634755657960e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b0383169081179091556040519081527f71614071b88dee5e0b2ae578a9dd7b2ebbe9ae832ba419dc0242cd065a290b6c906020015b60405180910390a150565b600154600160a01b900460ff1615611add576040516325dbe6e160e21b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b179055604051633db1a4d360e01b8152737b8bcf00def58b50620b2c253f3a97ee51f4468390633db1a4d390611b4e908590859060c0907f000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea90600401615819565b60006040518083038186803b158015611b6657600080fd5b505af4158015611b7a573d6000803e3d6000fd5b50505050600082806020019051810190611b949190615a3a565b9050611ba88160200151826000015161377b565b611bbf8160200151826040015183606001516137f2565b6000611c167f00000000000000000000000018f2cba9254d0b8854180cacad7b0567b39f44c66001600160a01b0316636352211e84602001516040518263ffffffff1660e01b8152600401610c6691815260200190565b90506000611c358260c00151846080015161350390919063ffffffff16565b9050611c6681601260ff7f000000000000000000000000000000000000000000000000000000000000000616613508565b8260a001818151611c7791906158e2565b905250608083015160c083015260a083015160009015611cc257611c9a83613070565b611cbf838560400151600014611cb4578560a00151611cb8565b6000195b6000613877565b90505b611ccb836136d8565b611cd984602001513361450c565b8360a00151600003611d3e5783516020808601516040808801516060808a0151835196875294860193909352908401528201527f3b963caf777561b495590bdc6ec8abd33838524d0a3aafc3f6f1b7857449d0999060800160405180910390a1611dad565b83516020808601516040808801516060808a015189840151845197885295870194909452918501528301526001600160a01b0316608082015260a081018290527fc72ca5dc9c24b437f43b8c112e7b4e24b4a7bce4b1f405e591eee1cc32d327c29060c0015b60405180910390a15b50506001805460ff60a01b1916905550505050565b600084600003611e7e576040516335313c2160e11b81523360048201527f00000000000000000000000055746ac5c15be05b43d7392f0e3867e86f573a666001600160a01b031690636a627842906024016020604051808303816000875af1158015611e32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e569190615902565b94508115611e79576000858152600460205260409020611e79906008600161459f565b611e88565b611e8885336145a8565b611e9184614668565b6000611e9e338787612f68565b90506000196001600160a01b03861601611ed35734841115611ed3576040516394613e9960e01b815260040160405180910390fd5b83600003611ef4576040516394613e9960e01b815260040160405180910390fd5b6001600160a01b038516600114611f1c578051611f1c906001600160a01b03871690866146ac565b611f268185614736565b611f2f816136d8565b6000611f3a8761342f565b60408051828152602081018a90526001600160a01b038916818301526060810188905290519192507f8eee26bd33caee967b96e711aa45ef94c734251b0457a0f2213fb392c01ef53a919081900360800190a186925050505b949350505050565b604051632a8bb6c960e11b815260026004828101919091526024820152604481018290526000908190737b8bcf00def58b50620b2c253f3a97ee51f44683906355176d92906064016040805180830381865af4158015611fff573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120239190615ab3565b91509150915091565b600154600160a01b900460ff1615612057576040516325dbe6e160e21b815260040160405180910390fd5b6001805460ff60a01b1916600160a01b179055604051633db1a4d360e01b8152737b8bcf00def58b50620b2c253f3a97ee51f4468390633db1a4d3906120c8908590859060a0907f000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea90600401615819565b60006040518083038186803b1580156120e057600080fd5b505af41580156120f4573d6000803e3d6000fd5b5050505060008280602001905181019061210e9190615ad7565b90506121228160200151826000015161377b565b60006121797f00000000000000000000000055746ac5c15be05b43d7392f0e3867e86f573a666001600160a01b0316636352211e84602001516040518263ffffffff1660e01b8152600401610c6691815260200190565b905060006121988260c00151846060015161350390919063ffffffff16565b90506121c981601260ff7f000000000000000000000000000000000000000000000000000000000000000616613508565b8260a0018181516121da91906158e2565b905250606083015160c08301526121f082613070565b60006122028385608001516001613877565b90508360400151612212846130b0565b1015612231576040516341c092a960e01b815260040160405180910390fd5b61223a836136d8565b61224884602001513361450c565b83516020808601516040808701518151948552928401919091526001600160a01b0390911690820152606081018290527f39f415ac8ef6b49b1ba40c4fafb2b911df1c5306c49eef15c4bc38a0128fca9890608001611da4565b6000546001600160a01b031633146122cd57604051634755657960e01b815260040160405180910390fd5b6040516330af10eb60e21b81526002600482015260248101829052737b8bcf00def58b50620b2c253f3a97ee51f446839063c2bc43ac9060440161160c565b826000036123a5576040516335313c2160e11b81523360048201527f00000000000000000000000018f2cba9254d0b8854180cacad7b0567b39f44c66001600160a01b031690636a627842906024016020604051808303816000875af115801561237a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061239e9190615902565b92506123af565b6123af8333612d80565b6123b882614668565b60006123c5338585612f68565b6001600090815260056020527f1471eb6eb2c5e789fc3de43f8ce62938c7d1836ec861730447e2ada8fd81017b5491925090612402908690612e44565b90506000196001600160a01b0385160161241a578092505b8260000361243b576040516394613e9960e01b815260040160405180910390fd5b6001600160a01b038416600114612463578151612463906001600160a01b03861690856146ac565b61246d8284614736565b61247682613070565b6000612481836130b0565b905061248c836136d8565b60006124978761342f565b60c08086015160608088015160408051868152602081018e9052908101889052918201929092526080810191909152600060a08201529192507f0be79565c9c0f7b4144002bd9abeb6bd0e8f43023abae7066992c58bdff9397e9101610a8d565b6000546001600160a01b0316331461252357604051634755657960e01b815260040160405180910390fd5b604051633c5d969360e01b8152600360048201526001600160a01b037f000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f429811660248301527f000000000000000000000000bca4439e99091afb297ecb4c5672357e467664f2811660448301527f00000000000000000000000093e6e4d43f34c1bf9385ecbb0fce912537ecf56a811660648301527f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b8116608483015280861660a4830152841660c482015260e481018390526101048101829052737b8bcf00def58b50620b2c253f3a97ee51f4468390633c5d9693906101240160006040518083038186803b15801561263557600080fd5b505af4158015612649573d6000803e3d6000fd5b5050505050505050565b6000546001600160a01b0316331461267e57604051634755657960e01b815260040160405180910390fd5b60408051633db1a4d360e01b8152737b8bcf00def58b50620b2c253f3a97ee51f4468391633db1a4d3916126db9186918691907f000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea90600401615819565b60006040518083038186803b1580156126f357600080fd5b505af4158015612707573d6000803e3d6000fd5b505050506000828060200190518101906127219190615b46565b8051909150461461273157600080fd5b602081015160405163ac46ae5160e01b8152600260048201526001600160a01b037f00000000000000000000000093e6e4d43f34c1bf9385ecbb0fce912537ecf56a811660248301527f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b811660448301527f00000000000000000000000042dd5168eda433d9c7523ad11ea15264343b18621660648201526084810191909152737b8bcf00def58b50620b2c253f3a97ee51f446839063ac46ae519060a4016117f3565b6000546001600160a01b0316331461282057604051634755657960e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f6b70829fcbe4891157f7a7496f9870927de3c8237adbe9cd39bae09b7382c40990602001611aa7565b6128b660405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b604051637d4b09b360e01b815260026004820152737b8bcf00def58b50620b2c253f3a97ee51f4468390637d4b09b39060240161010060405180830381865af4158015612907573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061292b9190615b94565b905090565b61293a86336145a8565b600560008190526020527f458b30c2d72bfd2c6317304a4594ecbafe5f729d3111b65fdc3a33bd48e5432d54612971908790612e44565b5083600003612993576040516394613e9960e01b815260040160405180910390fd5b60006129a0338888612f68565b90506129ab81613070565b60006129b6826130b0565b905060006129c483886131f7565b90506129d16064836157a2565b81116129db575060005b60006129e68a61342f565b90507ecae5d4a40f5ab841867a076a796230392d8ce403d409f84f18377b49a602d5818b848760c0015188606001518d8d8d8d604051612a2e99989796959493929190615c3a565b60405180910390a150505050505050505050565b604051630afc7a3960e31b815260056004820152606090737b8bcf00def58b50620b2c253f3a97ee51f44683906357e3d1c890602401600060405180830381865af4158015612a95573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261292b9190810190615c89565b612ac783336145a8565b600360005260056020527fa9bc9a3a348c357ba16b37005d7e6b3236198c0e939f4af8c5f19b8deeb8ebc054612afe908490612e44565b5080600003612b20576040516394613e9960e01b815260040160405180910390fd5b6000612b2d338585612f68565b9050612b3881613070565b6000612b43826130b0565b90506000612b5183856131f7565b9050612b5e6064836157a2565b8111612b68575060005b6000612b738761342f565b60c08086015160608088015160408051868152602081018e905290810188905291820192909252608081019190915260a081018890529192507f88ad2ade0e436c3cffc2b0d1b51fb1379e5bca5dd3aaef801b234b18aa87d2a99101610a8d565b612c276040518061010001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160001515815260200160008152602001600081525090565b60405163ee55e7b560e01b81526003600480830191909152602482015260448101839052737b8bcf00def58b50620b2c253f3a97ee51f446839063ee55e7b59060640161010060405180830381865af4158015612c88573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109749190615d22565b612cb684336145a8565b600460005260056020527f3eec716f11ba9e820c81ca75eb978ffb45831ef8b7a53e5e422c26008e1ca6d554612ced908590612e44565b50600084815260046020908152604080832060028452909152812054612d169033908790612f68565b9050612d2181613070565b6000612d2c826130b0565b90506000612d398761342f565b90507fd8eb4847ae2a642c0c24f4336eb3ac86a4cac889da47bb554f95c4d6163d80ff8188848660c0015187606001518b8b8b604051610a8d989796959493929190615da4565b806001600160a01b03167f00000000000000000000000018f2cba9254d0b8854180cacad7b0567b39f44c66001600160a01b0316636352211e846040518263ffffffff1660e01b8152600401612dd891815260200190565b602060405180830381865afa158015612df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e1991906158c5565b6001600160a01b031614612e405760405163a03dca6f60e01b815260040160405180910390fd5b5050565b6006600090815260026020527f59dd4b18488d12f51eda69757a0ed42a2010c14b564330cc74a06895e60c077b5482341015612e9357604051639413602360e01b815260040160405180910390fd5b6000612e9f8285615999565b6007600090815260026020527facd8ef244210bb6898e73c48bf820ed8ecc857a3bab8d79c10e4fa92b1e9ca655491925090612edc9083906157b6565b9050612eeb6002600783613760565b60008681526004602090815260408083206009845290915280822054600a8352912054612f1882826157b6565b6000898152600460205260409020859350909150612f3890600984613760565b6000888152600460205260409020612f5290600a83613760565b612f5c8734615999565b98975050505050505050565b612fd260405180610120016040528060006001600160a01b031681526020016000815260200160006001600160a01b031681526020016000815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b038481168252602080830185905283821660408085018290527fe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e05460608601526000918252600380845281832060018452845281832054909416608086015286825260048084528183209483528484528183205460a0870152825292909152205460c08201526130698383614997565b9392505050565b6040818101516001600160a01b031660009081526003602081815283832091835252205460e082015260408101516130a790614a95565b61010090910152565b600080670de0b6b3a76400008360e00151670de0b6b3a764000085610100015186608001516001600160a01b0316631e01043988602001516040518263ffffffff1660e01b815260040161310691815260200190565b602060405180830381865afa158015613123573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131479190615902565b6131519190615dea565b61315b91906157a2565b6131659190615dea565b61316f91906157a2565b90506000808460a00151121561319a576131958460a0015161319090615e01565b61358f565b61319d565b60005b90508082106131f0576131ed7f000000000000000000000000000000000000000000000000000000000000000660ff1660126131e68760a0015186614c1390919063ffffffff16565b9190614c40565b92505b5050919050565b600082610100015160001961320c91906157a2565b8210156109745760808301516020840151604051631e01043960e01b815260048101919091526000916001600160a01b031690631e01043990602401602060405180830381865afa158015613265573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132899190615902565b90508083106133295760008460a001511315613324576000670de0b6b3a764000085610100015183866132bc9190615999565b6132c69190615dea565b6132d091906157a2565b905060006132e18660a0015161358f565b9050818111156133215761331e60ff7f00000000000000000000000000000000000000000000000000000000000000061660126131e68585615999565b93505b50505b613428565b6000670de0b6b3a76400008560e00151670de0b6b3a764000087610100015187866133549190615999565b61335e9190615dea565b61336891906157a2565b6133729190615dea565b61337c91906157a2565b905060008560a00151126133d1576133ca7f000000000000000000000000000000000000000000000000000000000000000660ff1660126131e68860a0015185614c1390919063ffffffff16565b9250613426565b60006133e48660a0015161319090615e01565b9050808211156134245761342160ff7f00000000000000000000000000000000000000000000000000000000000000061660126131e68486615999565b93505b505b505b5092915050565b6005600090815260026020527fb98b78633099fa36ed8b8680c4f8092689e1e04080eb9cbb077ca38a14d7e38454613468600182615e1d565b9050613480600260056001600160801b038416613760565b60008381526004602090815260408083206001845290915281205490506134a8600182615e1d565b60008581526004602052604090209091506134ce9060016001600160801b038416613760565b60006134fa6001600160801b0383166fffffffffffffffffffffffffffffffff19608086901b166157b6565b95945050505050565b900390565b600080613516858585613698565b905060008512801561353257508461352f828587613698565b14155b15611f93576134fa60018261591b565b60006001600160ff1b0382111561357457604051632a26bd1360e01b8152600481018390526024015b60405180910390fd5b5090565b6000818313156135885781613069565b5090919050565b60008082121561357457604051632a33bb3160e01b81526004810183905260240161356b565b60006135c084614c73565b90506000196001600160a01b0385160161364e576000836001600160a01b03168360405160006040518083038185875af1925050503d8060008114613621576040519150601f19603f3d011682016040523d82523d6000602084013e613626565b606091505b50509050806136485760405163e277d13760e01b815260040160405180910390fd5b50613662565b6136626001600160a01b0385168484614cfa565b600061366d85614c73565b905061367983826157b6565b82146116385760405163f4d4667360e01b815260040160405180910390fd5b60008183146136d0576136ac83600a615f21565b6136b783600a615f21565b6136c1908661593b565b6136cb919061596b565b611f93565b509192915050565b60608101516136ec90600290600190613760565b604080820151602080840151600090815260049091529190912061371291600290614d5d565b60a0810151602080830151600090815260049091526040902061373791600390613760565b60c081015160208083015160009081526004918290526040902061375d92909190613760565b50565b805b60ff909216600090815260209390935250604090912055565b60008281526004602090815260408083206001845290915290205481906001600160801b038216146137c0576040516302e8145360e61b815260040160405180910390fd5b6137ed60016137cf8382615e1d565b600086815260046020526040902091906001600160801b0316613760565b505050565b6000806137fe85611f9b565b909250905061380f60028042613760565b61381c6002600385613760565b6138296002600484613760565b600085815260046020526040902061384390600586613760565b600085815260046020526040902061385d90600683613760565b600085815260046020526040902061163890600784613760565b60007f000000000000000000000000740e33ea864a1ee3d6c0a76f281ba1940859bf626001600160a01b031663c19d93fb6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156138d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138fb9190615f2d565b1561390557600080fd5b600061393260027f0000000000000000000000000000000000000000000000000000000000000006615f4a565b61393d90600a615f63565b8492509050613956670de0b6b3a76400006000196157a2565b82108015613968575060008560a00151125b15613a24577f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b6001600160a01b031685604001516001600160a01b0316036139cc576139bb8560a0015161319090615e01565b6139c590836157b6565b9150613a24565b6064856101000151670de0b6b3a76400006139ee8860a0015161319090615e01565b6139f89190615dea565b613a0291906157a2565b613a0d906069615dea565b613a1791906157a2565b613a2190836157b6565b91505b60808501516020860151604051637cbc237360e01b81526001600160a01b0390921691637cbc237391613a64918690600401918252602082015260400190565b6020604051808303816000875af1158015613a83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613aa79190615902565b9150600080600086851115613cb7576000613ac28887615999565b905060007f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b6001600160a01b03168a604001516001600160a01b031603613b16575080613b0f8188615999565b9650613c8b565b60016001600160a01b03168a604001516001600160a01b031603613bd6576000807f000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f4296001600160a01b031663f29216e2856040518263ffffffff1660e01b8152600401604080518083038185885af1158015613b96573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613bbb9190615ab3565b9093508392509050613bcd818a615999565b98505050613c8b565b60408a8101519051633e87f49360e01b81526001600160a01b0391821660048201526024810184905260009182917f000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f42990911690633e87f4939060440160408051808303816000875af1158015613c50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c749190615ab3565b9093508392509050613c86818a615999565b985050505b613c9581866157b6565b9450613ca081613542565b8a60a001818151613cb191906158e2565b90525050505b600085118015613ccb575060008860a00151125b15613f08576000613ce38960a0015161319090615e01565b905060007f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b6001600160a01b03168a604001516001600160a01b031603613d4757818710613d3e575080613d378188615999565b9650613edc565b50600095613edc565b85821015613d53578591505b60016001600160a01b03168a604001516001600160a01b031603613e20576000807f000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f4296001600160a01b0316637661f4f68a866040518363ffffffff1660e01b8152600401613dc391815260200190565b604080518083038185885af1158015613de0573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613e059190615ab3565b9093508392509050613e17818a615999565b98505050613edc565b60408a810151905163bf441a5960e01b81526001600160a01b039182166004820152602481018490526044810189905260009182917f000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f4299091169063bf441a599060640160408051808303816000875af1158015613ea1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ec59190615ab3565b9093508392509050613ed7818a615999565b985050505b613ee681866157b6565b9450613ef181613542565b8a60a001818151613f0291906158e2565b90525050505b60008860a001511315614116576000613f2b670de0b6b3a76400006000196157a2565b8810613f4557613f3e8960a0015161358f565b9050613fb0565b7f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b6001600160a01b031689604001516001600160a01b0316148015613f8957508786105b15613fb057613fad613f9e8a60a0015161358f565b613fa8888b615999565b614d6c565b90505b8015614114576000848211156140c75760006001600160a01b037f00000000000000000000000093e6e4d43f34c1bf9385ecbb0fce912537ecf56a16637cbc237382613ffc8987615999565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af115801561403f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140639190615902565b905061406f8684615999565b8110156140b157881561409857806140878785615999565b6140919190615999565b93506140b1565b6040516320b7831f60e11b815260040160405180910390fd5b6140bb81876157b6565b915060009550506140d6565b50806140d38186615999565b94505b6140e081856157b6565b93506140eb83613542565b6140f482613542565b6140fe91906158e2565b8a60a00181815161410f919061591b565b905250505b505b82156141b057604051631c57762b60e31b815260006004820152602481018490527f00000000000000000000000093e6e4d43f34c1bf9385ecbb0fce912537ecf56a6001600160a01b03169063e2bbb158906044016020604051808303816000875af115801561418a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141ae9190615902565b505b8115614451578515614246577f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b6001600160a01b031688604001516001600160a01b03160361420a5761420382866157b6565b9450614451565b8751614241906001600160a01b037f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b1690846135b5565b614451565b7f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b6001600160a01b031688604001516001600160a01b03160361428d5761420382866157b6565b838210156142e4576142da6142d17f000000000000000000000000000000000000000000000000000000000000000660ff1660126142ca86613542565b9190613698565b60608a01510190565b6060890152614451565b60016001600160a01b031688604001516001600160a01b0316036143a557604051632cb4d9d960e11b8152600481018390526000907f000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f4296001600160a01b031690635969b3b29060240160408051808303816000875af115801561436b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061438f9190615ab3565b915061439d905081876157b6565b955050614451565b604088810151905163045608af60e01b81526001600160a01b039182166004820152602481018490526000917f000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f429169063045608af9060440160408051808303816000875af115801561441b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061443f9190615ab3565b915061444d905081876157b6565b9550505b84156144745787516040890151614474916001600160a01b0390911690876135b5565b80156145015787516040516340c10f1960e01b81526001600160a01b039182166004820152602481018390527f000000000000000000000000b5284ed1606b91e0129182d55ee7ee31c31c920c909116906340c10f1990604401600060405180830381600087803b1580156144e857600080fd5b505af11580156144fc573d6000803e3d6000fd5b505050505b505050509392505050565b60008281526004602090815260408083206009845290915290205480156137ed57600760005260026020527facd8ef244210bb6898e73c48bf820ed8ecc857a3bab8d79c10e4fa92b1e9ca65546145638282615999565b90506145726002600783613760565b600084815260046020908152604080832060098452909152812055614599600184846135b5565b50505050565b61376281614d7c565b806001600160a01b03167f00000000000000000000000055746ac5c15be05b43d7392f0e3867e86f573a666001600160a01b0316636352211e846040518263ffffffff1660e01b815260040161460091815260200190565b602060405180830381865afa15801561461d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061464191906158c5565b6001600160a01b031614612e4057604051639289a15b60e01b815260040160405180910390fd5b6001600160a01b038181166000908152600360209081526040808320600184529091529020541661375d57604051634033aec960e01b815260040160405180910390fd5b6000196001600160a01b038416016146de573481146137ed57604051630fc9bd0f60e11b815260040160405180910390fd5b60006146e984614c73565b90506147006001600160a01b038516843085614d96565b600061470b85614c73565b905061471783836157b6565b811461163857604051630fc9bd0f60e11b815260040160405180910390fd5b7f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b6001600160a01b031682604001516001600160a01b031603614873576000670de0b6b3a76400006147a87f00000000000000000000000000000000000000000000000002c68af0bb14000084615dea565b6147b291906157a2565b90506147be8183615999565b604051631c57762b60e31b815260006004820152602481018390529092507f00000000000000000000000093e6e4d43f34c1bf9385ecbb0fce912537ecf56a6001600160a01b03169063e2bbb158906044016020604051808303816000875af115801561482f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148539190615902565b5061485d81613542565b8360a00181815161486e91906158e2565b905250505b60016001600160a01b031682604001516001600160a01b0316036149145781608001516001600160a01b031663e2bbb158828460200151846040518463ffffffff1660e01b81526004016148d1929190918252602082015260400190565b60206040518083038185885af11580156148ef573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906137ed9190615902565b60808201516020830151604051631c57762b60e31b81526001600160a01b039092169163e2bbb15891614954918590600401918252602082015260400190565b6020604051808303816000875af1158015614973573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137ed9190615902565b6000828152600460209081526040808320600284529091529020546001600160a01b038116158015906149dc5750816001600160a01b0316816001600160a01b031614155b156137ed576001600160a01b0381166000908152600360209081526040808320600184529091528120546001600160a01b0316635d988967856040518263ffffffff1660e01b8152600401614a3391815260200190565b602060405180830381865afa158015614a50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a749190615902565b9050801561459957604051634033aec960e01b815260040160405180910390fd5b60007f00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b6001600160a01b0316826001600160a01b031603614adf5750670de0b6b3a7640000919050565b6000614af3836001600160a01b0316614dce565b9050614be98160ff167f000000000000000000000000000000000000000000000000000000000000000660ff166131e67f000000000000000000000000bca4439e99091afb297ecb4c5672357e467664f26001600160a01b03166369843940614b8a6002600360008c6001600160a01b03166001600160a01b03168152602001908152602001600020614e4b90919063ffffffff16565b6040518263ffffffff1660e01b8152600401614ba891815260200190565b602060405180830381865afa158015614bc5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131909190615902565b915081600003614c0c5760405163089112d560e21b815260040160405180910390fd5b505b919050565b6000808212614c2d57614c2682846157b6565b9050610974565b614c3682615e01565b614c269084615999565b60008183146136d057614c5483600a615f21565b614c5f83600a615f21565b614c699086615dea565b6136cb91906157a2565b60006001600160a01b038216600114614cf3576040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015614cca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614cee9190615902565b610974565b4792915050565b6040516001600160a01b0383166024820152604481018290526137ed90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614e61565b6001600160a01b038116613762565b6000818311156135885781613069565b600081614d8a576000614d8d565b60015b60ff1692915050565b6040516001600160a01b03808516602483015283166044820152606481018290526145999085906323b872dd60e01b90608401614d26565b60006001600160a01b038216600114614e4357816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015614e1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614cee9190615f72565b601292915050565b60ff166000908152602091909152604090205490565b6000614eb6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614f369092919063ffffffff16565b9050805160001480614ed7575080806020019051810190614ed79190615f2d565b6137ed5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161356b565b6060611f93848460008585600080866001600160a01b03168587604051614f5d9190615f8f565b60006040518083038185875af1925050503d8060008114614f9a576040519150601f19603f3d011682016040523d82523d6000602084013e614f9f565b606091505b5091509150614fb087838387614fbb565b979650505050505050565b6060831561502a578251600003615023576001600160a01b0385163b6150235760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161356b565b5081611f93565b611f93838381511561503f5781518083602001fd5b8060405162461bcd60e51b815260040161356b9190615fab565b6001600160a01b038116811461375d57600080fd5b60006020828403121561508057600080fd5b813561306981615059565b6000806000606084860312156150a057600080fd5b8335925060208401356150b281615059565b929592945050506040919091013590565b60008083601f8401126150d557600080fd5b5081356001600160401b038111156150ec57600080fd5b6020830191508360208260051b850101111561510757600080fd5b9250929050565b801515811461375d57600080fd5b600080600080600080600060c0888a03121561513757600080fd5b87359650602088013561514981615059565b9550604088013594506060880135935060808801356001600160401b0381111561517257600080fd5b61517e8a828b016150c3565b90945092505060a08801356151928161510e565b8091505092959891949750929550565b634e487b7160e01b600052604160045260246000fd5b60405161014081016001600160401b03811182821017156151db576151db6151a2565b60405290565b60405161010081016001600160401b03811182821017156151db576151db6151a2565b604051601f8201601f191681016001600160401b038111828210171561522c5761522c6151a2565b604052919050565b600082601f83011261524557600080fd5b81356001600160401b0381111561525e5761525e6151a2565b615271601f8201601f1916602001615204565b81815284602083860101111561528657600080fd5b816020850160208301376000918101602001919091529392505050565b600080604083850312156152b657600080fd5b82356001600160401b03808211156152cd57600080fd5b6152d986838701615234565b935060208501359150808211156152ef57600080fd5b506152fc85828601615234565b9150509250929050565b6000806040838503121561531957600080fd5b82359150602083013561532b8161510e565b809150509250929050565b6000806040838503121561534957600080fd5b50508035926020909101359150565b60006020828403121561536a57600080fd5b5035919050565b60ff8116811461375d57600080fd5b60008060006060848603121561539557600080fd5b83356153a081615059565b925060208401356150b281615371565b815181526020808301516101408301916153d4908401826001600160a01b03169052565b5060408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525061012080840151818401525092915050565b6000806000806080858703121561544557600080fd5b84359350602085013561545781615059565b925060408501359150606085013561546e8161510e565b939692955090935050565b6000806000806080858703121561548f57600080fd5b843561549a81615059565b935060208501356154aa81615059565b93969395505050506040820135916060013590565b81516001600160a01b031681526101e0810160208301516154eb60208401826001600160a01b03169052565b50604083015161550660408401826001600160a01b03169052565b50606083015161552160608401826001600160a01b03169052565b50608083015161553c60808401826001600160a01b03169052565b5060a083015161555760a08401826001600160a01b03169052565b5060c083015161557260c08401826001600160a01b03169052565b5060e083015161558d60e08401826001600160a01b03169052565b506101008381015190830152610120808401519083015261014080840151908301526101608084015190830152610180808401516001600160a01b0381168285015250506101a0838101516001600160a01b0381168483015250506101c0838101516001600160a01b03811684830152613426565b60008060008060008060a0878903121561561b57600080fd5b86359550602087013561562d81615059565b9450604087013593506060870135925060808701356001600160401b0381111561565657600080fd5b61566289828a016150c3565b979a9699509497509295939492505050565b6020808252825182820181905260009190848201906040850190845b818110156156ac57835183529284019291840191600101615690565b50909695505050505050565b600080600080606085870312156156ce57600080fd5b843593506020850135925060408501356001600160401b038111156156f257600080fd5b6156fe878288016150c3565b95989497509550505050565b8051614c0e81615059565b60006060828403121561572757600080fd5b604051606081018181106001600160401b0382111715615749576157496151a2565b604052825161575781615059565b8152602083810151908201526040928301519281019290925250919050565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000826157b1576157b1615776565b500490565b808201808211156109745761097461578c565b60005b838110156157e45781810151838201526020016157cc565b50506000910152565b600081518084526158058160208601602086016157c9565b601f01601f19169290920160200192915050565b60808152600061582c60808301876157ed565b828103602084015261583e81876157ed565b604084019590955250506001600160a01b039190911660609091015292915050565b60006080828403121561587257600080fd5b604051608081018181106001600160401b0382111715615894576158946151a2565b8060405250825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b6000602082840312156158d757600080fd5b815161306981615059565b80820182811260008312801582168215821617156134265761342661578c565b60006020828403121561591457600080fd5b5051919050565b81810360008312801583831316838312821617156134285761342861578c565b80820260008212600160ff1b841416156159575761595761578c565b81810583148215176109745761097461578c565b60008261597a5761597a615776565b600160ff1b8214600019841416156159945761599461578c565b500590565b818103818111156109745761097461578c565b600061014082840312156159bf57600080fd5b6159c76151b8565b825181526159d76020840161570a565b602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152506101208084015181830152508091505092915050565b600060c08284031215615a4c57600080fd5b60405160c081018181106001600160401b0382111715615a6e57615a6e6151a2565b8060405250825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a08201528091505092915050565b60008060408385031215615ac657600080fd5b505080516020909101519092909150565b600060a08284031215615ae957600080fd5b60405160a081018181106001600160401b0382111715615b0b57615b0b6151a2565b806040525082518152602083015160208201526040830151604082015260608301516060820152608083015160808201528091505092915050565b600060408284031215615b5857600080fd5b604051604081018181106001600160401b0382111715615b7a57615b7a6151a2565b604052825181526020928301519281019290925250919050565b60006101008284031215615ba757600080fd5b615baf6151e1565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201528091505092915050565b8183526000602080850194508260005b85811015615c2f57813587529582019590820190600101615c13565b509495945050505050565b60006101008b83528a60208401528960408401528860608401528760808401528660a08401528560c08401528060e0840152615c798184018587615c03565b9c9b505050505050505050505050565b60006020808385031215615c9c57600080fd5b82516001600160401b0380821115615cb357600080fd5b818501915085601f830112615cc757600080fd5b815181811115615cd957615cd96151a2565b8060051b9150615cea848301615204565b8181529183018401918481019088841115615d0457600080fd5b938501935b83851015612f5c57845182529385019390850190615d09565b60006101008284031215615d3557600080fd5b615d3d6151e1565b825181526020830151615d4f81615059565b8060208301525060408301516040820152606083015160608201526080830151608082015260a0830151615d828161510e565b60a082015260c0838101519082015260e0928301519281019290925250919050565b8881528760208201528660408201528560608201528460808201528360a082015260e060c08201526000615ddc60e083018486615c03565b9a9950505050505050505050565b80820281158282048414176109745761097461578c565b6000600160ff1b8201615e1657615e1661578c565b5060000390565b6001600160801b038181168382160190808211156134285761342861578c565b600181815b80851115615e78578160001904821115615e5e57615e5e61578c565b80851615615e6b57918102915b93841c9390800290615e42565b509250929050565b600082615e8f57506001610974565b81615e9c57506000610974565b8160018114615eb25760028114615ebc57615ed8565b6001915050610974565b60ff841115615ecd57615ecd61578c565b50506001821b610974565b5060208310610133831016604e8410600b8410161715615efb575081810a610974565b615f058383615e3d565b8060001904821115615f1957615f1961578c565b029392505050565b60006130698383615e80565b600060208284031215615f3f57600080fd5b81516130698161510e565b60ff82811682821603908111156109745761097461578c565b600061306960ff841683615e80565b600060208284031215615f8457600080fd5b815161306981615371565b60008251615fa18184602087016157c9565b9190910192915050565b60208152600061306960208301846157ed56fea26469706673582212202b223b63ea2397fb0b32e8c02881d93adcd67c430dd2ba9f7ec10e13cc0847cd64736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000018f2cba9254d0b8854180cacad7b0567b39f44c600000000000000000000000055746ac5c15be05b43d7392f0e3867e86f573a66000000000000000000000000bca4439e99091afb297ecb4c5672357e467664f2000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f42900000000000000000000000093e6e4d43f34c1bf9385ecbb0fce912537ecf56a000000000000000000000000b5284ed1606b91e0129182d55ee7ee31c31c920c00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea00000000000000000000000000000000000000000000000002c68af0bb14000000000000000000000000000000000000000000000000000006f05b59d3b200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001dcd650000000000000000000000000042dd5168eda433d9c7523ad11ea15264343b186200000000000000000000000051393080c546623d04ccacf86428664856c7ac76000000000000000000000000740e33ea864a1ee3d6c0a76f281ba1940859bf62
-----Decoded View---------------
Arg [0] : p (tuple):
Arg [1] : lToken (address): 0x18f2CbA9254d0b8854180CACAd7b0567B39F44c6
Arg [2] : pToken (address): 0x55746AC5C15bE05B43D7392f0E3867E86f573A66
Arg [3] : oracle (address): 0xbCa4439E99091aFB297ecB4C5672357E467664f2
Arg [4] : swapper (address): 0x763f02688cc0c78e8c4E852302bFAe6e1B33f429
Arg [5] : vault0 (address): 0x93e6e4d43F34c1bF9385ECBB0fCE912537Ecf56a
Arg [6] : iou (address): 0xB5284Ed1606b91e0129182d55eE7EE31c31c920c
Arg [7] : tokenB0 (address): 0x07d83526730c7438048D55A4fc0b850e2aaB6f0b
Arg [8] : dChainEventSigner (address): 0xa51Cd97F3090f6a16Cf0cdC12B0cB4b0a95b38EA
Arg [9] : b0ReserveRatio (uint256): 200000000000000000
Arg [10] : liquidationRewardCutRatio (int256): 500000000000000000
Arg [11] : minLiquidationReward (int256): 0
Arg [12] : maxLiquidationReward (int256): 500000000
Arg [13] : protocolFeeManager (address): 0x42Dd5168Eda433d9C7523AD11Ea15264343b1862
Arg [14] : liqClaim (address): 0x51393080C546623d04CcAcf86428664856C7AC76
Arg [15] : switchOracle (address): 0x740e33Ea864A1EE3D6C0a76f281Ba1940859BF62
-----Encoded View---------------
15 Constructor Arguments found :
Arg [0] : 00000000000000000000000018f2cba9254d0b8854180cacad7b0567b39f44c6
Arg [1] : 00000000000000000000000055746ac5c15be05b43d7392f0e3867e86f573a66
Arg [2] : 000000000000000000000000bca4439e99091afb297ecb4c5672357e467664f2
Arg [3] : 000000000000000000000000763f02688cc0c78e8c4e852302bfae6e1b33f429
Arg [4] : 00000000000000000000000093e6e4d43f34c1bf9385ecbb0fce912537ecf56a
Arg [5] : 000000000000000000000000b5284ed1606b91e0129182d55ee7ee31c31c920c
Arg [6] : 00000000000000000000000007d83526730c7438048d55a4fc0b850e2aab6f0b
Arg [7] : 000000000000000000000000a51cd97f3090f6a16cf0cdc12b0cb4b0a95b38ea
Arg [8] : 00000000000000000000000000000000000000000000000002c68af0bb140000
Arg [9] : 00000000000000000000000000000000000000000000000006f05b59d3b20000
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [11] : 000000000000000000000000000000000000000000000000000000001dcd6500
Arg [12] : 00000000000000000000000042dd5168eda433d9c7523ad11ea15264343b1862
Arg [13] : 00000000000000000000000051393080c546623d04ccacf86428664856c7ac76
Arg [14] : 000000000000000000000000740e33ea864a1ee3d6c0a76f281ba1940859bf62
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.