Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- PancakeStableSwapTwoPoolInfo
- Optimization enabled
- true
- Compiler version
- v0.8.10+commit.fc410830
- Optimization runs
- 1000
- EVM Version
- default
- Verified at
- 2024-11-08T12:29:30.187141Z
contracts/PancakeStableSwapTwoPoolInfo.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/IPancakeStableSwap.sol";
contract PancakeStableSwapTwoPoolInfo {
uint256 public constant N_COINS = 2;
uint256 public constant FEE_DENOMINATOR = 1e10;
uint256 public constant PRECISION = 1e18;
function token(address _swap) public view returns (IERC20) {
return IERC20(IPancakeStableSwap(_swap).token());
}
function balances(address _swap) public view returns (uint256[N_COINS] memory swapBalances) {
for (uint256 i = 0; i < N_COINS; i++) {
swapBalances[i] = IPancakeStableSwap(_swap).balances(i);
}
}
function RATES(address _swap) public view returns (uint256[N_COINS] memory swapRATES) {
for (uint256 i = 0; i < N_COINS; i++) {
swapRATES[i] = IPancakeStableSwap(_swap).RATES(i);
}
}
function PRECISION_MUL(address _swap) public view returns (uint256[N_COINS] memory swapPRECISION_MUL) {
for (uint256 i = 0; i < N_COINS; i++) {
swapPRECISION_MUL[i] = IPancakeStableSwap(_swap).PRECISION_MUL(i);
}
}
function calc_coins_amount(address _swap, uint256 _amount) external view returns (uint256[N_COINS] memory) {
uint256 total_supply = token(_swap).totalSupply();
uint256[N_COINS] memory amounts;
for (uint256 i = 0; i < N_COINS; i++) {
uint256 value = (IPancakeStableSwap(_swap).balances(i) * _amount) / total_supply;
amounts[i] = value;
}
return amounts;
}
function get_D_mem(
address _swap,
uint256[N_COINS] memory _balances,
uint256 amp
) public view returns (uint256) {
return get_D(_xp_mem(_swap, _balances), amp);
}
function get_add_liquidity_mint_amount(address _swap, uint256[N_COINS] memory amounts)
external
view
returns (uint256)
{
IPancakeStableSwap swap = IPancakeStableSwap(_swap);
uint256[N_COINS] memory fees;
uint256 _fee = (swap.fee() * N_COINS) / (4 * (N_COINS - 1));
uint256 amp = swap.A();
uint256 token_supply = token(_swap).totalSupply();
//Initial invariant
uint256 D0;
uint256[N_COINS] memory old_balances = balances(_swap);
if (token_supply > 0) {
D0 = get_D_mem(_swap, old_balances, amp);
}
uint256[N_COINS] memory new_balances = [old_balances[0], old_balances[1]];
for (uint256 i = 0; i < N_COINS; i++) {
if (token_supply == 0) {
require(amounts[i] > 0, "Initial deposit requires all coins");
}
// balances store amounts of c-tokens
new_balances[i] = old_balances[i] + amounts[i];
}
// Invariant after change
uint256 D1 = get_D_mem(_swap, new_balances, amp);
require(D1 > D0, "D1 must be greater than D0");
// We need to recalculate the invariant accounting for fees
// to calculate fair user's share
uint256 D2 = D1;
if (token_supply > 0) {
// Only account for fees if we are not the first to deposit
for (uint256 i = 0; i < N_COINS; i++) {
uint256 ideal_balance = (D1 * old_balances[i]) / D0;
uint256 difference;
if (ideal_balance > new_balances[i]) {
difference = ideal_balance - new_balances[i];
} else {
difference = new_balances[i] - ideal_balance;
}
fees[i] = (_fee * difference) / FEE_DENOMINATOR;
new_balances[i] -= fees[i];
}
D2 = get_D_mem(_swap, new_balances, amp);
}
// Calculate, how much pool tokens to mint
uint256 mint_amount;
if (token_supply == 0) {
mint_amount = D1; // Take the dust if there was any
} else {
mint_amount = (token_supply * (D2 - D0)) / D0;
}
return mint_amount;
}
function get_add_liquidity_fee(address _swap, uint256[N_COINS] memory amounts)
external
view
returns (uint256[N_COINS] memory liquidityFee)
{
IPancakeStableSwap swap = IPancakeStableSwap(_swap);
uint256 _fee = (swap.fee() * N_COINS) / (4 * (N_COINS - 1));
uint256 _admin_fee = swap.admin_fee();
uint256 amp = swap.A();
uint256 token_supply = token(_swap).totalSupply();
//Initial invariant
uint256 D0;
uint256[N_COINS] memory old_balances = balances(_swap);
if (token_supply > 0) {
D0 = get_D_mem(_swap, old_balances, amp);
}
uint256[N_COINS] memory new_balances = [old_balances[0], old_balances[1]];
for (uint256 i = 0; i < N_COINS; i++) {
if (token_supply == 0) {
require(amounts[i] > 0, "Initial deposit requires all coins");
}
new_balances[i] = old_balances[i] + amounts[i];
}
// Invariant after change
uint256 D1 = get_D_mem(_swap, new_balances, amp);
require(D1 > D0, "D1 must be greater than D0");
if (token_supply > 0) {
// Only account for fees if we are not the first to deposit
for (uint256 i = 0; i < N_COINS; i++) {
uint256 ideal_balance = (D1 * old_balances[i]) / D0;
uint256 difference;
if (ideal_balance > new_balances[i]) {
difference = ideal_balance - new_balances[i];
} else {
difference = new_balances[i] - ideal_balance;
}
uint256 coinFee;
coinFee = (_fee * difference) / FEE_DENOMINATOR;
liquidityFee[i] = ((coinFee * _admin_fee) / FEE_DENOMINATOR);
}
}
}
function get_remove_liquidity_imbalance_fee(address _swap, uint256[N_COINS] memory amounts)
external
view
returns (uint256[N_COINS] memory liquidityFee)
{
IPancakeStableSwap swap = IPancakeStableSwap(_swap);
uint256 _fee = (swap.fee() * N_COINS) / (4 * (N_COINS - 1));
uint256 _admin_fee = swap.admin_fee();
uint256 amp = swap.A();
uint256[N_COINS] memory old_balances = balances(_swap);
uint256[N_COINS] memory new_balances = [old_balances[0], old_balances[1]];
uint256 D0 = get_D_mem(_swap, old_balances, amp);
for (uint256 i = 0; i < N_COINS; i++) {
new_balances[i] -= amounts[i];
}
uint256 D1 = get_D_mem(_swap, new_balances, amp);
for (uint256 i = 0; i < N_COINS; i++) {
uint256 ideal_balance = (D1 * old_balances[i]) / D0;
uint256 difference;
if (ideal_balance > new_balances[i]) {
difference = ideal_balance - new_balances[i];
} else {
difference = new_balances[i] - ideal_balance;
}
uint256 coinFee;
coinFee = (_fee * difference) / FEE_DENOMINATOR;
liquidityFee[i] = ((coinFee * _admin_fee) / FEE_DENOMINATOR);
}
}
function _xp_mem(address _swap, uint256[N_COINS] memory _balances)
public
view
returns (uint256[N_COINS] memory result)
{
result = RATES(_swap);
for (uint256 i = 0; i < N_COINS; i++) {
result[i] = (result[i] * _balances[i]) / PRECISION;
}
}
function get_D(uint256[N_COINS] memory xp, uint256 amp) internal pure returns (uint256) {
uint256 S;
for (uint256 i = 0; i < N_COINS; i++) {
S += xp[i];
}
if (S == 0) {
return 0;
}
uint256 Dprev;
uint256 D = S;
uint256 Ann = amp * N_COINS;
for (uint256 j = 0; j < 255; j++) {
uint256 D_P = D;
for (uint256 k = 0; k < N_COINS; k++) {
D_P = (D_P * D) / (xp[k] * N_COINS); // If division by 0, this will be borked: only withdrawal will work. And that is good
}
Dprev = D;
D = ((Ann * S + D_P * N_COINS) * D) / ((Ann - 1) * D + (N_COINS + 1) * D_P);
// Equality with the precision of 1
if (D > Dprev) {
if (D - Dprev <= 1) {
break;
}
} else {
if (Dprev - D <= 1) {
break;
}
}
}
return D;
}
function get_y(
address _swap,
uint256 i,
uint256 j,
uint256 x,
uint256[N_COINS] memory xp_
) internal view returns (uint256) {
IPancakeStableSwap swap = IPancakeStableSwap(_swap);
uint256 amp = swap.A();
uint256 D = get_D(xp_, amp);
uint256 c = D;
uint256 S_;
uint256 Ann = amp * N_COINS;
uint256 _x;
for (uint256 k = 0; k < N_COINS; k++) {
if (k == i) {
_x = x;
} else if (k != j) {
_x = xp_[k];
} else {
continue;
}
S_ += _x;
c = (c * D) / (_x * N_COINS);
}
c = (c * D) / (Ann * N_COINS);
uint256 b = S_ + D / Ann; // - D
uint256 y_prev;
uint256 y = D;
for (uint256 m = 0; m < 255; m++) {
y_prev = y;
y = (y * y + c) / (2 * y + b - D);
// Equality with the precision of 1
if (y > y_prev) {
if (y - y_prev <= 1) {
break;
}
} else {
if (y_prev - y <= 1) {
break;
}
}
}
return y;
}
function get_exchange_fee(
address _swap,
uint256 i,
uint256 j,
uint256 dx
) external view returns (uint256 exFee, uint256 exAdminFee) {
IPancakeStableSwap swap = IPancakeStableSwap(_swap);
uint256[N_COINS] memory old_balances = balances(_swap);
uint256[N_COINS] memory xp = _xp_mem(_swap, old_balances);
uint256[N_COINS] memory rates = RATES(_swap);
uint256 x = xp[i] + (dx * rates[i]) / PRECISION;
uint256 y = get_y(_swap, i, j, x, xp);
uint256 dy = xp[j] - y - 1; // -1 just in case there were some rounding errors
uint256 dy_fee = (dy * swap.fee()) / FEE_DENOMINATOR;
uint256 dy_admin_fee = (dy_fee * swap.admin_fee()) / FEE_DENOMINATOR;
dy_fee = (dy_fee * PRECISION) / rates[j];
dy_admin_fee = (dy_admin_fee * PRECISION) / rates[j];
exFee = dy_fee;
exAdminFee = dy_admin_fee;
}
function _xp(address _swap) internal view returns (uint256[N_COINS] memory result) {
result = RATES(_swap);
for (uint256 i = 0; i < N_COINS; i++) {
result[i] = (result[i] * IPancakeStableSwap(_swap).balances(i)) / PRECISION;
}
}
function get_y_D(
uint256 A_,
uint256 i,
uint256[N_COINS] memory xp,
uint256 D
) internal pure returns (uint256) {
/**
Calculate x[i] if one reduces D from being calculated for xp to D
Done by solving quadratic equation iteratively.
x_1**2 + x1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
x_1**2 + b*x_1 = c
x_1 = (x_1**2 + c) / (2*x_1 + b)
*/
// x in the input is converted to the same price/precision
require(i < N_COINS, "dev: i above N_COINS");
uint256 c = D;
uint256 S_;
uint256 Ann = A_ * N_COINS;
uint256 _x;
for (uint256 k = 0; k < N_COINS; k++) {
if (k != i) {
_x = xp[k];
} else {
continue;
}
S_ += _x;
c = (c * D) / (_x * N_COINS);
}
c = (c * D) / (Ann * N_COINS);
uint256 b = S_ + D / Ann;
uint256 y_prev;
uint256 y = D;
for (uint256 k = 0; k < 255; k++) {
y_prev = y;
y = (y * y + c) / (2 * y + b - D);
// Equality with the precision of 1
if (y > y_prev) {
if (y - y_prev <= 1) {
break;
}
} else {
if (y_prev - y <= 1) {
break;
}
}
}
return y;
}
function _calc_withdraw_one_coin(
address _swap,
uint256 _token_amount,
uint256 i
) internal view returns (uint256, uint256) {
IPancakeStableSwap swap = IPancakeStableSwap(_swap);
uint256 amp = swap.A();
uint256 _fee = (swap.fee() * N_COINS) / (4 * (N_COINS - 1));
uint256[N_COINS] memory precisions = PRECISION_MUL(_swap);
uint256[N_COINS] memory xp = _xp(_swap);
uint256 D0 = get_D(xp, amp);
uint256 D1 = D0 - (_token_amount * D0) / (token(_swap).totalSupply());
uint256[N_COINS] memory xp_reduced = xp;
uint256 new_y = get_y_D(amp, i, xp, D1);
uint256 dy_0 = (xp[i] - new_y) / precisions[i]; // w/o fees
for (uint256 k = 0; k < N_COINS; k++) {
uint256 dx_expected;
if (k == i) {
dx_expected = (xp[k] * D1) / D0 - new_y;
} else {
dx_expected = xp[k] - (xp[k] * D1) / D0;
}
xp_reduced[k] -= (_fee * dx_expected) / FEE_DENOMINATOR;
}
uint256 dy = xp_reduced[i] - get_y_D(amp, i, xp_reduced, D1);
dy = (dy - 1) / precisions[i]; // Withdraw less to account for rounding errors
return (dy, dy_0 - dy);
}
function get_remove_liquidity_one_coin_fee(
address _swap,
uint256 _token_amount,
uint256 i
) external view returns (uint256 adminFee) {
IPancakeStableSwap swap = IPancakeStableSwap(_swap);
(, uint256 dy_fee) = _calc_withdraw_one_coin(_swap, _token_amount, i);
adminFee = (dy_fee * swap.admin_fee()) / FEE_DENOMINATOR;
}
function get_dx(
address _swap,
uint256 i,
uint256 j,
uint256 dy,
uint256 max_dx
) external view returns (uint256) {
IPancakeStableSwap swap = IPancakeStableSwap(_swap);
uint256[N_COINS] memory old_balances = balances(_swap);
uint256[N_COINS] memory xp = _xp_mem(_swap, old_balances);
uint256 dy_with_fee = (dy * FEE_DENOMINATOR) / (FEE_DENOMINATOR - swap.fee());
require(dy_with_fee < old_balances[j], "Excess balance");
uint256[N_COINS] memory rates = RATES(_swap);
uint256 y = xp[j] - (dy_with_fee * rates[j]) / PRECISION;
uint256 x = get_y(_swap, j, i, y, xp);
uint256 dx = x - xp[i];
// Convert all to real units
dx = (dx * PRECISION) / rates[i] + 1; // +1 for round lose.
require(dx <= max_dx, "Exchange resulted in fewer coins than expected");
return dx;
}
}
@openzeppelin/contracts/token/ERC20/IERC20.sol
// 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);
}
@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol
// 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);
}
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
// 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));
}
}
@openzeppelin/contracts/utils/Address.sol
// 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);
}
}
}
interfaces/IPancakeStableSwap.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.5;
interface IPancakeStableSwap {
function token() external view returns (address);
function balances(uint256 i) external view returns (uint256);
function N_COINS() external view returns (uint256);
function RATES(uint256 i) external view returns (uint256);
function coins(uint256 i) external view returns (address);
function PRECISION_MUL(uint256 i) external view returns (uint256);
function fee() external view returns (uint256);
function admin_fee() external view returns (uint256);
function A() external view returns (uint256);
function get_D_mem(uint256[2] memory _balances, uint256 amp) external view returns (uint256);
function get_y(
uint256 i,
uint256 j,
uint256 x,
uint256[2] memory xp_
) external view returns (uint256);
function calc_withdraw_one_coin(uint256 _token_amount, uint256 i) external view returns (uint256);
}
Compiler Settings
{"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"],"":["ast"]}},"optimizer":{"runs":1000,"enabled":true},"libraries":{}}
Contract ABI
[{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"FEE_DENOMINATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"N_COINS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"PRECISION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[2]","name":"swapPRECISION_MUL","internalType":"uint256[2]"}],"name":"PRECISION_MUL","inputs":[{"type":"address","name":"_swap","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[2]","name":"swapRATES","internalType":"uint256[2]"}],"name":"RATES","inputs":[{"type":"address","name":"_swap","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[2]","name":"result","internalType":"uint256[2]"}],"name":"_xp_mem","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256[2]","name":"_balances","internalType":"uint256[2]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[2]","name":"swapBalances","internalType":"uint256[2]"}],"name":"balances","inputs":[{"type":"address","name":"_swap","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[2]","name":"","internalType":"uint256[2]"}],"name":"calc_coins_amount","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"get_D_mem","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256[2]","name":"_balances","internalType":"uint256[2]"},{"type":"uint256","name":"amp","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[2]","name":"liquidityFee","internalType":"uint256[2]"}],"name":"get_add_liquidity_fee","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256[2]","name":"amounts","internalType":"uint256[2]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"get_add_liquidity_mint_amount","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256[2]","name":"amounts","internalType":"uint256[2]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"get_dx","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256","name":"i","internalType":"uint256"},{"type":"uint256","name":"j","internalType":"uint256"},{"type":"uint256","name":"dy","internalType":"uint256"},{"type":"uint256","name":"max_dx","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"exFee","internalType":"uint256"},{"type":"uint256","name":"exAdminFee","internalType":"uint256"}],"name":"get_exchange_fee","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256","name":"i","internalType":"uint256"},{"type":"uint256","name":"j","internalType":"uint256"},{"type":"uint256","name":"dx","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[2]","name":"liquidityFee","internalType":"uint256[2]"}],"name":"get_remove_liquidity_imbalance_fee","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256[2]","name":"amounts","internalType":"uint256[2]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"adminFee","internalType":"uint256"}],"name":"get_remove_liquidity_one_coin_fee","inputs":[{"type":"address","name":"_swap","internalType":"address"},{"type":"uint256","name":"_token_amount","internalType":"uint256"},{"type":"uint256","name":"i","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"token","inputs":[{"type":"address","name":"_swap","internalType":"address"}]}]
Contract Creation Code
0x608060405234801561001057600080fd5b50612616806100206000396000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c80636c92118e11610097578063b6f03ac111610066578063b6f03ac11461022b578063ca4bc7141461023e578063d73792a914610251578063f74d92e71461025d57600080fd5b80636c92118e146101cb5780636d46a1db146101de578063aaf5eb6814610209578063ae6452b41461021857600080fd5b8063279a07cc116100d3578063279a07cc1461017c57806327e235e31461018f57806329357750146101a25780635779dcd8146101b857600080fd5b80630d098fa91461010557806315a4d7fe1461012e5780631f38b0c7146101415780632494acbd14610169575b600080fd5b610118610113366004612349565b610270565b604051610125919061237f565b60405180910390f35b61011861013c3660046123b0565b61071b565b61015461014f3660046123dc565b610864565b60408051928352602083019190915201610125565b610118610177366004612417565b610aa9565b61011861018a366004612417565b610b6e565b61011861019d366004612417565b610c2d565b6101aa600281565b604051908152602001610125565b6101aa6101c636600461243b565b610cd3565b6101186101d9366004612349565b610d6b565b6101f16101ec366004612417565b6110af565b6040516001600160a01b039091168152602001610125565b6101aa670de0b6b3a764000081565b610118610226366004612349565b611113565b6101aa610239366004612349565b6111ad565b6101aa61024c366004612470565b61164d565b6101aa6402540be40081565b6101aa61026b3660046124b4565b6118af565b610278612295565b82600061028760016002612509565b610292906004612520565b6002836001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102f6919061253f565b6103009190612520565b61030a9190612558565b90506000826001600160a01b031663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa15801561034c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610370919061253f565b90506000836001600160a01b031663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103d6919061253f565b905060006103e3886110af565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610420573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610444919061253f565b90506000806104528a610c2d565b90508215610468576104658a82866118af565b91505b60006040518060400160405280836000600281106104885761048861257a565b60200201518152602001836001600281106104a5576104a561257a565b60200201519052905060005b600281101561059657846105355760008b82600281106104d3576104d361257a565b6020020151116105355760405162461bcd60e51b815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f696044820152616e7360f01b60648201526084015b60405180910390fd5b8a81600281106105475761054761257a565b602002015183826002811061055e5761055e61257a565b602002015161056d9190612590565b82826002811061057f5761057f61257a565b60200201528061058e816125a8565b9150506104b1565b5060006105a48c83886118af565b90508381116105f55760405162461bcd60e51b815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e204430000000000000604482015260640161052c565b841561070c5760005b600281101561070a5760008585836002811061061c5761061c61257a565b602002015161062b9085612520565b6106359190612558565b9050600084836002811061064b5761064b61257a565b602002015182111561067f578483600281106106695761066961257a565b60200201516106789083612509565b90506106a4565b818584600281106106925761069261257a565b60200201516106a19190612509565b90505b60006402540be4006106b6838e612520565b6106c09190612558565b90506402540be4006106d28c83612520565b6106dc9190612558565b8e85600281106106ee576106ee61257a565b6020020152508291506107029050816125a8565b9150506105fe565b505b50505050505050505092915050565b610723612295565b600061072e846110af565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561076b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078f919061253f565b9050610799612295565b60005b60028110156108595760008386886001600160a01b0316634903b0d1856040518263ffffffff1660e01b81526004016107d791815260200190565b602060405180830381865afa1580156107f4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610818919061253f565b6108229190612520565b61082c9190612558565b9050808383600281106108415761084161257a565b60200201525080610851816125a8565b91505061079c565b509150505b92915050565b600080858161087282610c2d565b905060006108808983611113565b9050600061088d8a610aa9565b90506000670de0b6b3a7640000828b600281106108ac576108ac61257a565b60200201516108bb908a612520565b6108c59190612558565b838b600281106108d7576108d761257a565b60200201516108e69190612590565b905060006108f78c8c8c85886118cc565b90506000600182868d600281106109105761091061257a565b602002015161091f9190612509565b6109299190612509565b905060006402540be400886001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610971573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610995919061253f565b61099f9084612520565b6109a99190612558565b905060006402540be400896001600160a01b031663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a15919061253f565b610a1f9084612520565b610a299190612558565b9050858d60028110610a3d57610a3d61257a565b6020020151610a54670de0b6b3a764000084612520565b610a5e9190612558565b9150858d60028110610a7257610a7261257a565b6020020151610a89670de0b6b3a764000083612520565b610a939190612558565b919f919e50909c50505050505050505050505050565b610ab1612295565b60005b6002811015610b68576040517f62203d74000000000000000000000000000000000000000000000000000000008152600481018290526001600160a01b038416906362203d7490602401602060405180830381865afa158015610b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3f919061253f565b828260028110610b5157610b5161257a565b602002015280610b60816125a8565b915050610ab4565b50919050565b610b76612295565b60005b6002811015610b68576040517f7dafa364000000000000000000000000000000000000000000000000000000008152600481018290526001600160a01b03841690637dafa36490602401602060405180830381865afa158015610be0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c04919061253f565b828260028110610c1657610c1661257a565b602002015280610c25816125a8565b915050610b79565b610c35612295565b60005b6002811015610b6857604051634903b0d160e01b8152600481018290526001600160a01b03841690634903b0d190602401602060405180830381865afa158015610c86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610caa919061253f565b828260028110610cbc57610cbc61257a565b602002015280610ccb816125a8565b915050610c38565b60008381610ce2828686611acc565b9150506402540be400826001600160a01b031663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d4d919061253f565b610d579083612520565b610d619190612558565b9695505050505050565b610d73612295565b826000610d8260016002612509565b610d8d906004612520565b6002836001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dcd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610df1919061253f565b610dfb9190612520565b610e059190612558565b90506000826001600160a01b031663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6b919061253f565b90506000836001600160a01b031663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ead573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ed1919061253f565b90506000610ede88610c2d565b90506000604051806040016040528083600060028110610f0057610f0061257a565b6020020151815260200183600160028110610f1d57610f1d61257a565b6020020151905290506000610f338a84866118af565b905060005b6002811015610f9057898160028110610f5357610f5361257a565b6020020151838260028110610f6a57610f6a61257a565b60200201818151610f7b9190612509565b90525080610f88816125a8565b915050610f38565b506000610f9e8b84876118af565b905060005b600281101561070c57600083868360028110610fc157610fc161257a565b6020020151610fd09085612520565b610fda9190612558565b90506000858360028110610ff057610ff061257a565b60200201518211156110245785836002811061100e5761100e61257a565b602002015161101d9083612509565b9050611049565b818684600281106110375761103761257a565b60200201516110469190612509565b90505b60006402540be40061105b838d612520565b6110659190612558565b90506402540be4006110778b83612520565b6110819190612558565b8d85600281106110935761109361257a565b6020020152508291506110a79050816125a8565b915050610fa3565b6000816001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110ef573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085e91906125c3565b61111b612295565b61112483610aa9565b905060005b60028110156111a657670de0b6b3a764000083826002811061114d5761114d61257a565b60200201518383600281106111645761116461257a565b60200201516111739190612520565b61117d9190612558565b82826002811061118f5761118f61257a565b60200201528061119e816125a8565b915050611129565b5092915050565b6000826111b8612295565b60006111c660016002612509565b6111d1906004612520565b6002846001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611211573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611235919061253f565b61123f9190612520565b6112499190612558565b90506000836001600160a01b031663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561128b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112af919061253f565b905060006112bc886110af565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061131d919061253f565b905060008061132b8a610c2d565b905082156113415761133e8a82866118af565b91505b60006040518060400160405280836000600281106113615761136161257a565b602002015181526020018360016002811061137e5761137e61257a565b60200201519052905060005b600281101561146a57846114095760008b82600281106113ac576113ac61257a565b6020020151116114095760405162461bcd60e51b815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f696044820152616e7360f01b606482015260840161052c565b8a816002811061141b5761141b61257a565b60200201518382600281106114325761143261257a565b60200201516114419190612590565b8282600281106114535761145361257a565b602002015280611462816125a8565b91505061138a565b5060006114788c83886118af565b90508381116114c95760405162461bcd60e51b815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e204430000000000000604482015260640161052c565b80851561160c5760005b60028110156115fd576000868683600281106114f1576114f161257a565b60200201516115009086612520565b61150a9190612558565b905060008583600281106115205761152061257a565b60200201518211156115545785836002811061153e5761153e61257a565b602002015161154d9083612509565b9050611579565b818684600281106115675761156761257a565b60200201516115769190612509565b90505b6402540be400611589828d612520565b6115939190612558565b8c84600281106115a5576115a561257a565b60200201528b83600281106115bc576115bc61257a565b60200201518684600281106115d3576115d361257a565b602002018181516115e49190612509565b9052508291506115f59050816125a8565b9150506114d3565b506116098d84896118af565b90505b60008661161a57508161163c565b856116258184612509565b61162f9089612520565b6116399190612558565b90505b9d9c50505050505050505050505050565b6000858161165a82610c2d565b905060006116688983611113565b90506000836001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116ce919061253f565b6116dd906402540be400612509565b6116ec6402540be40089612520565b6116f69190612558565b905082886002811061170a5761170a61257a565b6020020151811061175d5760405162461bcd60e51b815260206004820152600e60248201527f4578636573732062616c616e6365000000000000000000000000000000000000604482015260640161052c565b60006117688b610aa9565b90506000670de0b6b3a7640000828b600281106117875761178761257a565b60200201516117969085612520565b6117a09190612558565b848b600281106117b2576117b261257a565b60200201516117c19190612509565b905060006117d28d8c8e85896118cc565b90506000858d600281106117e8576117e861257a565b60200201516117f79083612509565b9050838d6002811061180b5761180b61257a565b6020020151611822670de0b6b3a764000083612520565b61182c9190612558565b611837906001612590565b90508981111561163c5760405162461bcd60e51b815260206004820152602e60248201527f45786368616e676520726573756c74656420696e20666577657220636f696e7360448201527f207468616e206578706563746564000000000000000000000000000000000000606482015260840161052c565b60006118c46118be8585611113565b83611e55565b949350505050565b6000808690506000816001600160a01b031663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611912573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611936919061253f565b905060006119448583611e55565b905080600080611955600286612520565b90506000805b60028110156119de578c811415611974578a915061199e565b8b81146119995789816002811061198d5761198d61257a565b6020020151915061199e565b6119cc565b6119a88285612590565b93506119b5600283612520565b6119bf8787612520565b6119c99190612558565b94505b806119d6816125a8565b91505061195b565b506119ea600283612520565b6119f48686612520565b6119fe9190612558565b93506000611a0c8387612558565b611a169085612590565b9050600086815b60ff811015611ab8578192508884836002611a389190612520565b611a429190612590565b611a4c9190612509565b88611a578480612520565b611a619190612590565b611a6b9190612558565b915082821115611a90576001611a818484612509565b11611a8b57611ab8565b611aa6565b6001611a9c8385612509565b11611aa657611ab8565b80611ab0816125a8565b915050611a1d565b509f9e505050505050505050505050505050565b60008060008590506000816001600160a01b031663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b38919061253f565b90506000611b4860016002612509565b611b53906004612520565b6002846001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bb7919061253f565b611bc19190612520565b611bcb9190612558565b90506000611bd889610b6e565b90506000611be58a611feb565b90506000611bf38286611e55565b90506000611c008c6110af565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c61919061253f565b611c6b838d612520565b611c759190612558565b611c7f9083612509565b9050826000611c90888d84866120d1565b90506000868d60028110611ca657611ca661257a565b602002015182878f60028110611cbe57611cbe61257a565b6020020151611ccd9190612509565b611cd79190612558565b905060005b6002811015611dd35760008e821415611d2e578387878a8560028110611d0457611d0461257a565b6020020151611d139190612520565b611d1d9190612558565b611d279190612509565b9050611d7f565b8686898460028110611d4257611d4261257a565b6020020151611d519190612520565b611d5b9190612558565b888360028110611d6d57611d6d61257a565b6020020151611d7c9190612509565b90505b6402540be400611d8f828c612520565b611d999190612558565b858360028110611dab57611dab61257a565b60200201818151611dbc9190612509565b905250819050611dcb816125a8565b915050611cdc565b506000611de28a8f86886120d1565b848f60028110611df457611df461257a565b6020020151611e039190612509565b9050878e60028110611e1757611e1761257a565b6020020151611e27600183612509565b611e319190612558565b905080611e3e8184612509565b9c509c505050505050505050505050935093915050565b60008060005b6002811015611e9957848160028110611e7657611e7661257a565b6020020151611e859083612590565b915080611e91816125a8565b915050611e5b565b5080611ea957600091505061085e565b60008181611eb8600287612520565b905060005b60ff811015611fdf578260005b6002811015611f1e5760028a8260028110611ee757611ee761257a565b6020020151611ef69190612520565b611f008684612520565b611f0a9190612558565b915080611f16816125a8565b915050611eca565b508394508060026001611f319190612590565b611f3b9190612520565b84611f47600186612509565b611f519190612520565b611f5b9190612590565b84611f67600284612520565b611f718987612520565b611f7b9190612590565b611f859190612520565b611f8f9190612558565b935084841115611fb5576001611fa58686612509565b11611fb05750611fdf565b611fcc565b6001611fc18587612509565b11611fcc5750611fdf565b5080611fd7816125a8565b915050611ebd565b50909695505050505050565b611ff3612295565b611ffc82610aa9565b905060005b6002811015610b6857604051634903b0d160e01b815260048101829052670de0b6b3a7640000906001600160a01b03851690634903b0d190602401602060405180830381865afa158015612059573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061207d919061253f565b83836002811061208f5761208f61257a565b602002015161209e9190612520565b6120a89190612558565b8282600281106120ba576120ba61257a565b6020020152806120c9816125a8565b915050612001565b6000600284106121235760405162461bcd60e51b815260206004820152601460248201527f6465763a20692061626f7665204e5f434f494e53000000000000000000000000604482015260640161052c565b81600080612132600289612520565b90506000805b60028110156121ab578881146121665787816002811061215a5761215a61257a565b6020020151915061216b565b612199565b6121758285612590565b9350612182600283612520565b61218c8887612520565b6121969190612558565b94505b806121a3816125a8565b915050612138565b506121b7600283612520565b6121c18786612520565b6121cb9190612558565b935060006121d98388612558565b6121e39085612590565b9050600087815b60ff8110156122855781925089848360026122059190612520565b61220f9190612590565b6122199190612509565b886122248480612520565b61222e9190612590565b6122389190612558565b91508282111561225d57600161224e8484612509565b1161225857612285565b612273565b60016122698385612509565b1161227357612285565b8061227d816125a8565b9150506121ea565b509b9a5050505050505050505050565b60405180604001604052806002906020820280368337509192915050565b6001600160a01b03811681146122c857600080fd5b50565b600082601f8301126122dc57600080fd5b6040516040810181811067ffffffffffffffff8211171561230d57634e487b7160e01b600052604160045260246000fd5b806040525080604084018581111561232457600080fd5b845b8181101561233e578035835260209283019201612326565b509195945050505050565b6000806060838503121561235c57600080fd5b8235612367816122b3565b915061237684602085016122cb565b90509250929050565b60408101818360005b60028110156123a7578151835260209283019290910190600101612388565b50505092915050565b600080604083850312156123c357600080fd5b82356123ce816122b3565b946020939093013593505050565b600080600080608085870312156123f257600080fd5b84356123fd816122b3565b966020860135965060408601359560600135945092505050565b60006020828403121561242957600080fd5b8135612434816122b3565b9392505050565b60008060006060848603121561245057600080fd5b833561245b816122b3565b95602085013595506040909401359392505050565b600080600080600060a0868803121561248857600080fd5b8535612493816122b3565b97602087013597506040870135966060810135965060800135945092505050565b6000806000608084860312156124c957600080fd5b83356124d4816122b3565b92506124e385602086016122cb565b9150606084013590509250925092565b634e487b7160e01b600052601160045260246000fd5b60008282101561251b5761251b6124f3565b500390565b600081600019048311821515161561253a5761253a6124f3565b500290565b60006020828403121561255157600080fd5b5051919050565b60008261257557634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b600082198211156125a3576125a36124f3565b500190565b60006000198214156125bc576125bc6124f3565b5060010190565b6000602082840312156125d557600080fd5b8151612434816122b356fea2646970667358221220745a995eeec8d0a2f83224fc03a32bbfb32c90e7f22b8f0549444b48ab598ec764736f6c634300080a0033
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106101005760003560e01c80636c92118e11610097578063b6f03ac111610066578063b6f03ac11461022b578063ca4bc7141461023e578063d73792a914610251578063f74d92e71461025d57600080fd5b80636c92118e146101cb5780636d46a1db146101de578063aaf5eb6814610209578063ae6452b41461021857600080fd5b8063279a07cc116100d3578063279a07cc1461017c57806327e235e31461018f57806329357750146101a25780635779dcd8146101b857600080fd5b80630d098fa91461010557806315a4d7fe1461012e5780631f38b0c7146101415780632494acbd14610169575b600080fd5b610118610113366004612349565b610270565b604051610125919061237f565b60405180910390f35b61011861013c3660046123b0565b61071b565b61015461014f3660046123dc565b610864565b60408051928352602083019190915201610125565b610118610177366004612417565b610aa9565b61011861018a366004612417565b610b6e565b61011861019d366004612417565b610c2d565b6101aa600281565b604051908152602001610125565b6101aa6101c636600461243b565b610cd3565b6101186101d9366004612349565b610d6b565b6101f16101ec366004612417565b6110af565b6040516001600160a01b039091168152602001610125565b6101aa670de0b6b3a764000081565b610118610226366004612349565b611113565b6101aa610239366004612349565b6111ad565b6101aa61024c366004612470565b61164d565b6101aa6402540be40081565b6101aa61026b3660046124b4565b6118af565b610278612295565b82600061028760016002612509565b610292906004612520565b6002836001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102f6919061253f565b6103009190612520565b61030a9190612558565b90506000826001600160a01b031663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa15801561034c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610370919061253f565b90506000836001600160a01b031663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103d6919061253f565b905060006103e3886110af565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610420573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610444919061253f565b90506000806104528a610c2d565b90508215610468576104658a82866118af565b91505b60006040518060400160405280836000600281106104885761048861257a565b60200201518152602001836001600281106104a5576104a561257a565b60200201519052905060005b600281101561059657846105355760008b82600281106104d3576104d361257a565b6020020151116105355760405162461bcd60e51b815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f696044820152616e7360f01b60648201526084015b60405180910390fd5b8a81600281106105475761054761257a565b602002015183826002811061055e5761055e61257a565b602002015161056d9190612590565b82826002811061057f5761057f61257a565b60200201528061058e816125a8565b9150506104b1565b5060006105a48c83886118af565b90508381116105f55760405162461bcd60e51b815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e204430000000000000604482015260640161052c565b841561070c5760005b600281101561070a5760008585836002811061061c5761061c61257a565b602002015161062b9085612520565b6106359190612558565b9050600084836002811061064b5761064b61257a565b602002015182111561067f578483600281106106695761066961257a565b60200201516106789083612509565b90506106a4565b818584600281106106925761069261257a565b60200201516106a19190612509565b90505b60006402540be4006106b6838e612520565b6106c09190612558565b90506402540be4006106d28c83612520565b6106dc9190612558565b8e85600281106106ee576106ee61257a565b6020020152508291506107029050816125a8565b9150506105fe565b505b50505050505050505092915050565b610723612295565b600061072e846110af565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561076b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078f919061253f565b9050610799612295565b60005b60028110156108595760008386886001600160a01b0316634903b0d1856040518263ffffffff1660e01b81526004016107d791815260200190565b602060405180830381865afa1580156107f4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610818919061253f565b6108229190612520565b61082c9190612558565b9050808383600281106108415761084161257a565b60200201525080610851816125a8565b91505061079c565b509150505b92915050565b600080858161087282610c2d565b905060006108808983611113565b9050600061088d8a610aa9565b90506000670de0b6b3a7640000828b600281106108ac576108ac61257a565b60200201516108bb908a612520565b6108c59190612558565b838b600281106108d7576108d761257a565b60200201516108e69190612590565b905060006108f78c8c8c85886118cc565b90506000600182868d600281106109105761091061257a565b602002015161091f9190612509565b6109299190612509565b905060006402540be400886001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610971573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610995919061253f565b61099f9084612520565b6109a99190612558565b905060006402540be400896001600160a01b031663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a15919061253f565b610a1f9084612520565b610a299190612558565b9050858d60028110610a3d57610a3d61257a565b6020020151610a54670de0b6b3a764000084612520565b610a5e9190612558565b9150858d60028110610a7257610a7261257a565b6020020151610a89670de0b6b3a764000083612520565b610a939190612558565b919f919e50909c50505050505050505050505050565b610ab1612295565b60005b6002811015610b68576040517f62203d74000000000000000000000000000000000000000000000000000000008152600481018290526001600160a01b038416906362203d7490602401602060405180830381865afa158015610b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3f919061253f565b828260028110610b5157610b5161257a565b602002015280610b60816125a8565b915050610ab4565b50919050565b610b76612295565b60005b6002811015610b68576040517f7dafa364000000000000000000000000000000000000000000000000000000008152600481018290526001600160a01b03841690637dafa36490602401602060405180830381865afa158015610be0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c04919061253f565b828260028110610c1657610c1661257a565b602002015280610c25816125a8565b915050610b79565b610c35612295565b60005b6002811015610b6857604051634903b0d160e01b8152600481018290526001600160a01b03841690634903b0d190602401602060405180830381865afa158015610c86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610caa919061253f565b828260028110610cbc57610cbc61257a565b602002015280610ccb816125a8565b915050610c38565b60008381610ce2828686611acc565b9150506402540be400826001600160a01b031663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d4d919061253f565b610d579083612520565b610d619190612558565b9695505050505050565b610d73612295565b826000610d8260016002612509565b610d8d906004612520565b6002836001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dcd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610df1919061253f565b610dfb9190612520565b610e059190612558565b90506000826001600160a01b031663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6b919061253f565b90506000836001600160a01b031663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ead573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ed1919061253f565b90506000610ede88610c2d565b90506000604051806040016040528083600060028110610f0057610f0061257a565b6020020151815260200183600160028110610f1d57610f1d61257a565b6020020151905290506000610f338a84866118af565b905060005b6002811015610f9057898160028110610f5357610f5361257a565b6020020151838260028110610f6a57610f6a61257a565b60200201818151610f7b9190612509565b90525080610f88816125a8565b915050610f38565b506000610f9e8b84876118af565b905060005b600281101561070c57600083868360028110610fc157610fc161257a565b6020020151610fd09085612520565b610fda9190612558565b90506000858360028110610ff057610ff061257a565b60200201518211156110245785836002811061100e5761100e61257a565b602002015161101d9083612509565b9050611049565b818684600281106110375761103761257a565b60200201516110469190612509565b90505b60006402540be40061105b838d612520565b6110659190612558565b90506402540be4006110778b83612520565b6110819190612558565b8d85600281106110935761109361257a565b6020020152508291506110a79050816125a8565b915050610fa3565b6000816001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110ef573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085e91906125c3565b61111b612295565b61112483610aa9565b905060005b60028110156111a657670de0b6b3a764000083826002811061114d5761114d61257a565b60200201518383600281106111645761116461257a565b60200201516111739190612520565b61117d9190612558565b82826002811061118f5761118f61257a565b60200201528061119e816125a8565b915050611129565b5092915050565b6000826111b8612295565b60006111c660016002612509565b6111d1906004612520565b6002846001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611211573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611235919061253f565b61123f9190612520565b6112499190612558565b90506000836001600160a01b031663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561128b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112af919061253f565b905060006112bc886110af565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061131d919061253f565b905060008061132b8a610c2d565b905082156113415761133e8a82866118af565b91505b60006040518060400160405280836000600281106113615761136161257a565b602002015181526020018360016002811061137e5761137e61257a565b60200201519052905060005b600281101561146a57846114095760008b82600281106113ac576113ac61257a565b6020020151116114095760405162461bcd60e51b815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f696044820152616e7360f01b606482015260840161052c565b8a816002811061141b5761141b61257a565b60200201518382600281106114325761143261257a565b60200201516114419190612590565b8282600281106114535761145361257a565b602002015280611462816125a8565b91505061138a565b5060006114788c83886118af565b90508381116114c95760405162461bcd60e51b815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e204430000000000000604482015260640161052c565b80851561160c5760005b60028110156115fd576000868683600281106114f1576114f161257a565b60200201516115009086612520565b61150a9190612558565b905060008583600281106115205761152061257a565b60200201518211156115545785836002811061153e5761153e61257a565b602002015161154d9083612509565b9050611579565b818684600281106115675761156761257a565b60200201516115769190612509565b90505b6402540be400611589828d612520565b6115939190612558565b8c84600281106115a5576115a561257a565b60200201528b83600281106115bc576115bc61257a565b60200201518684600281106115d3576115d361257a565b602002018181516115e49190612509565b9052508291506115f59050816125a8565b9150506114d3565b506116098d84896118af565b90505b60008661161a57508161163c565b856116258184612509565b61162f9089612520565b6116399190612558565b90505b9d9c50505050505050505050505050565b6000858161165a82610c2d565b905060006116688983611113565b90506000836001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116ce919061253f565b6116dd906402540be400612509565b6116ec6402540be40089612520565b6116f69190612558565b905082886002811061170a5761170a61257a565b6020020151811061175d5760405162461bcd60e51b815260206004820152600e60248201527f4578636573732062616c616e6365000000000000000000000000000000000000604482015260640161052c565b60006117688b610aa9565b90506000670de0b6b3a7640000828b600281106117875761178761257a565b60200201516117969085612520565b6117a09190612558565b848b600281106117b2576117b261257a565b60200201516117c19190612509565b905060006117d28d8c8e85896118cc565b90506000858d600281106117e8576117e861257a565b60200201516117f79083612509565b9050838d6002811061180b5761180b61257a565b6020020151611822670de0b6b3a764000083612520565b61182c9190612558565b611837906001612590565b90508981111561163c5760405162461bcd60e51b815260206004820152602e60248201527f45786368616e676520726573756c74656420696e20666577657220636f696e7360448201527f207468616e206578706563746564000000000000000000000000000000000000606482015260840161052c565b60006118c46118be8585611113565b83611e55565b949350505050565b6000808690506000816001600160a01b031663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611912573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611936919061253f565b905060006119448583611e55565b905080600080611955600286612520565b90506000805b60028110156119de578c811415611974578a915061199e565b8b81146119995789816002811061198d5761198d61257a565b6020020151915061199e565b6119cc565b6119a88285612590565b93506119b5600283612520565b6119bf8787612520565b6119c99190612558565b94505b806119d6816125a8565b91505061195b565b506119ea600283612520565b6119f48686612520565b6119fe9190612558565b93506000611a0c8387612558565b611a169085612590565b9050600086815b60ff811015611ab8578192508884836002611a389190612520565b611a429190612590565b611a4c9190612509565b88611a578480612520565b611a619190612590565b611a6b9190612558565b915082821115611a90576001611a818484612509565b11611a8b57611ab8565b611aa6565b6001611a9c8385612509565b11611aa657611ab8565b80611ab0816125a8565b915050611a1d565b509f9e505050505050505050505050505050565b60008060008590506000816001600160a01b031663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b38919061253f565b90506000611b4860016002612509565b611b53906004612520565b6002846001600160a01b031663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b93573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bb7919061253f565b611bc19190612520565b611bcb9190612558565b90506000611bd889610b6e565b90506000611be58a611feb565b90506000611bf38286611e55565b90506000611c008c6110af565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c61919061253f565b611c6b838d612520565b611c759190612558565b611c7f9083612509565b9050826000611c90888d84866120d1565b90506000868d60028110611ca657611ca661257a565b602002015182878f60028110611cbe57611cbe61257a565b6020020151611ccd9190612509565b611cd79190612558565b905060005b6002811015611dd35760008e821415611d2e578387878a8560028110611d0457611d0461257a565b6020020151611d139190612520565b611d1d9190612558565b611d279190612509565b9050611d7f565b8686898460028110611d4257611d4261257a565b6020020151611d519190612520565b611d5b9190612558565b888360028110611d6d57611d6d61257a565b6020020151611d7c9190612509565b90505b6402540be400611d8f828c612520565b611d999190612558565b858360028110611dab57611dab61257a565b60200201818151611dbc9190612509565b905250819050611dcb816125a8565b915050611cdc565b506000611de28a8f86886120d1565b848f60028110611df457611df461257a565b6020020151611e039190612509565b9050878e60028110611e1757611e1761257a565b6020020151611e27600183612509565b611e319190612558565b905080611e3e8184612509565b9c509c505050505050505050505050935093915050565b60008060005b6002811015611e9957848160028110611e7657611e7661257a565b6020020151611e859083612590565b915080611e91816125a8565b915050611e5b565b5080611ea957600091505061085e565b60008181611eb8600287612520565b905060005b60ff811015611fdf578260005b6002811015611f1e5760028a8260028110611ee757611ee761257a565b6020020151611ef69190612520565b611f008684612520565b611f0a9190612558565b915080611f16816125a8565b915050611eca565b508394508060026001611f319190612590565b611f3b9190612520565b84611f47600186612509565b611f519190612520565b611f5b9190612590565b84611f67600284612520565b611f718987612520565b611f7b9190612590565b611f859190612520565b611f8f9190612558565b935084841115611fb5576001611fa58686612509565b11611fb05750611fdf565b611fcc565b6001611fc18587612509565b11611fcc5750611fdf565b5080611fd7816125a8565b915050611ebd565b50909695505050505050565b611ff3612295565b611ffc82610aa9565b905060005b6002811015610b6857604051634903b0d160e01b815260048101829052670de0b6b3a7640000906001600160a01b03851690634903b0d190602401602060405180830381865afa158015612059573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061207d919061253f565b83836002811061208f5761208f61257a565b602002015161209e9190612520565b6120a89190612558565b8282600281106120ba576120ba61257a565b6020020152806120c9816125a8565b915050612001565b6000600284106121235760405162461bcd60e51b815260206004820152601460248201527f6465763a20692061626f7665204e5f434f494e53000000000000000000000000604482015260640161052c565b81600080612132600289612520565b90506000805b60028110156121ab578881146121665787816002811061215a5761215a61257a565b6020020151915061216b565b612199565b6121758285612590565b9350612182600283612520565b61218c8887612520565b6121969190612558565b94505b806121a3816125a8565b915050612138565b506121b7600283612520565b6121c18786612520565b6121cb9190612558565b935060006121d98388612558565b6121e39085612590565b9050600087815b60ff8110156122855781925089848360026122059190612520565b61220f9190612590565b6122199190612509565b886122248480612520565b61222e9190612590565b6122389190612558565b91508282111561225d57600161224e8484612509565b1161225857612285565b612273565b60016122698385612509565b1161227357612285565b8061227d816125a8565b9150506121ea565b509b9a5050505050505050505050565b60405180604001604052806002906020820280368337509192915050565b6001600160a01b03811681146122c857600080fd5b50565b600082601f8301126122dc57600080fd5b6040516040810181811067ffffffffffffffff8211171561230d57634e487b7160e01b600052604160045260246000fd5b806040525080604084018581111561232457600080fd5b845b8181101561233e578035835260209283019201612326565b509195945050505050565b6000806060838503121561235c57600080fd5b8235612367816122b3565b915061237684602085016122cb565b90509250929050565b60408101818360005b60028110156123a7578151835260209283019290910190600101612388565b50505092915050565b600080604083850312156123c357600080fd5b82356123ce816122b3565b946020939093013593505050565b600080600080608085870312156123f257600080fd5b84356123fd816122b3565b966020860135965060408601359560600135945092505050565b60006020828403121561242957600080fd5b8135612434816122b3565b9392505050565b60008060006060848603121561245057600080fd5b833561245b816122b3565b95602085013595506040909401359392505050565b600080600080600060a0868803121561248857600080fd5b8535612493816122b3565b97602087013597506040870135966060810135965060800135945092505050565b6000806000608084860312156124c957600080fd5b83356124d4816122b3565b92506124e385602086016122cb565b9150606084013590509250925092565b634e487b7160e01b600052601160045260246000fd5b60008282101561251b5761251b6124f3565b500390565b600081600019048311821515161561253a5761253a6124f3565b500290565b60006020828403121561255157600080fd5b5051919050565b60008261257557634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b600082198211156125a3576125a36124f3565b500190565b60006000198214156125bc576125bc6124f3565b5060010190565b6000602082840312156125d557600080fd5b8151612434816122b356fea2646970667358221220745a995eeec8d0a2f83224fc03a32bbfb32c90e7f22b8f0549444b48ab598ec764736f6c634300080a0033