Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- NineinchStableSwapTwoPoolInfo
- Optimization enabled
- true
- Compiler version
- v0.8.10+commit.fc410830
- Optimization runs
- 1000000
- EVM Version
- istanbul
- Verified at
- 2024-05-07T17:53:18.627524Z
contracts/NineinchStableSwapTwoPoolInfo.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/INineinchStableSwap.sol";
contract NineinchStableSwapTwoPoolInfo {
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(INineinchStableSwap(_swap).token());
}
function balances(address _swap) public view returns (uint256[N_COINS] memory swapBalances) {
for (uint256 i = 0; i < N_COINS; i++) {
swapBalances[i] = INineinchStableSwap(_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] = INineinchStableSwap(_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] = INineinchStableSwap(_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 = (INineinchStableSwap(_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)
{
INineinchStableSwap swap = INineinchStableSwap(_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)
{
INineinchStableSwap swap = INineinchStableSwap(_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)
{
INineinchStableSwap swap = INineinchStableSwap(_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) {
INineinchStableSwap swap = INineinchStableSwap(_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) {
INineinchStableSwap swap = INineinchStableSwap(_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] * INineinchStableSwap(_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) {
INineinchStableSwap swap = INineinchStableSwap(_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) {
INineinchStableSwap swap = INineinchStableSwap(_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) {
INineinchStableSwap swap = INineinchStableSwap(_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;
}
}
contracts/interfaces/INineinchStableSwap.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
interface INineinchStableSwap {
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);
}
@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);
}
}
}
Compiler Settings
{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":1000000,"enabled":true},"metadata":{"bytecodeHash":"none"},"libraries":{},"evmVersion":"istanbul"}
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
0x608060405234801561001057600080fd5b5061290a806100206000396000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c80636c92118e11610097578063b6f03ac111610066578063b6f03ac114610238578063ca4bc7141461024b578063d73792a91461025e578063f74d92e71461026a57600080fd5b80636c92118e146101cb5780636d46a1db146101de578063aaf5eb6814610216578063ae6452b41461022557600080fd5b8063279a07cc116100d3578063279a07cc1461017c57806327e235e31461018f57806329357750146101a25780635779dcd8146101b857600080fd5b80630d098fa91461010557806315a4d7fe1461012e5780631f38b0c7146101415780632494acbd14610169575b600080fd5b6101186101133660046125df565b61027d565b6040516101259190612615565b60405180910390f35b61011861013c366004612646565b6107ab565b61015461014f366004612672565b61090e565b60408051928352602083019190915201610125565b6101186101773660046126ad565b610b6d565b61011861018a3660046126ad565b610c3f565b61011861019d3660046126ad565b610d0b565b6101aa600281565b604051908152602001610125565b6101aa6101c63660046126d1565b610dd7565b6101186101d93660046125df565b610e7c565b6101f16101ec3660046126ad565b6111e7565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610125565b6101aa670de0b6b3a764000081565b6101186102333660046125df565b611258565b6101aa6102463660046125df565b6112f2565b6101aa610259366004612706565b611808565b6101aa6402540be40081565b6101aa61027836600461274a565b611aab565b610285612505565b826000610294600160026127b8565b61029f9060046127cf565b60028373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610310919061280c565b61031a91906127cf565b6103249190612825565b905060008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610373573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610397919061280c565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061040a919061280c565b90506000610417886111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610461573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610485919061280c565b90506000806104938a610d0b565b905082156104a9576104a68a8286611aab565b91505b60006040518060400160405280836000600281106104c9576104c9612860565b60200201518152602001836001600281106104e6576104e6612860565b60200201519052905060005b600281101561060c57846105ab5760008b826002811061051457610514612860565b6020020151116105ab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f6960448201527f6e7300000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b8a81600281106105bd576105bd612860565b60200201518382600281106105d4576105d4612860565b60200201516105e3919061288f565b8282600281106105f5576105f5612860565b602002015280610604816128a7565b9150506104f2565b50600061061a8c8388611aab565b9050838111610685576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e20443000000000000060448201526064016105a2565b841561079c5760005b600281101561079a576000858583600281106106ac576106ac612860565b60200201516106bb90856127cf565b6106c59190612825565b905060008483600281106106db576106db612860565b602002015182111561070f578483600281106106f9576106f9612860565b602002015161070890836127b8565b9050610734565b8185846002811061072257610722612860565b602002015161073191906127b8565b90505b60006402540be400610746838e6127cf565b6107509190612825565b90506402540be4006107628c836127cf565b61076c9190612825565b8e856002811061077e5761077e612860565b6020020152508291506107929050816128a7565b91505061068e565b505b50505050505050505092915050565b6107b3612505565b60006107be846111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610808573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061082c919061280c565b9050610836612505565b60005b600281101561090357600083868873ffffffffffffffffffffffffffffffffffffffff16634903b0d1856040518263ffffffff1660e01b815260040161088191815260200190565b602060405180830381865afa15801561089e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c2919061280c565b6108cc91906127cf565b6108d69190612825565b9050808383600281106108eb576108eb612860565b602002015250806108fb816128a7565b915050610839565b509150505b92915050565b600080858161091c82610d0b565b9050600061092a8983611258565b905060006109378a610b6d565b90506000670de0b6b3a7640000828b6002811061095657610956612860565b6020020151610965908a6127cf565b61096f9190612825565b838b6002811061098157610981612860565b6020020151610990919061288f565b905060006109a18c8c8c8588611ac8565b90506000600182868d600281106109ba576109ba612860565b60200201516109c991906127b8565b6109d391906127b8565b905060006402540be4008873ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4c919061280c565b610a5690846127cf565b610a609190612825565b905060006402540be4008973ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ab5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad9919061280c565b610ae390846127cf565b610aed9190612825565b9050858d60028110610b0157610b01612860565b6020020151610b18670de0b6b3a7640000846127cf565b610b229190612825565b9150858d60028110610b3657610b36612860565b6020020151610b4d670de0b6b3a7640000836127cf565b610b579190612825565b919f919e50909c50505050505050505050505050565b610b75612505565b60005b6002811015610c39576040517f62203d740000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff8416906362203d7490602401602060405180830381865afa158015610bec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c10919061280c565b828260028110610c2257610c22612860565b602002015280610c31816128a7565b915050610b78565b50919050565b610c47612505565b60005b6002811015610c39576040517f7dafa3640000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff841690637dafa36490602401602060405180830381865afa158015610cbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ce2919061280c565b828260028110610cf457610cf4612860565b602002015280610d03816128a7565b915050610c4a565b610d13612505565b60005b6002811015610c39576040517f4903b0d10000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff841690634903b0d190602401602060405180830381865afa158015610d8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dae919061280c565b828260028110610dc057610dc0612860565b602002015280610dcf816128a7565b915050610d16565b60008381610de6828686611cd5565b9150506402540be4008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5e919061280c565b610e6890836127cf565b610e729190612825565b9695505050505050565b610e84612505565b826000610e93600160026127b8565b610e9e9060046127cf565b60028373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0f919061280c565b610f1991906127cf565b610f239190612825565b905060008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f96919061280c565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fe5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611009919061280c565b9050600061101688610d0b565b9050600060405180604001604052808360006002811061103857611038612860565b602002015181526020018360016002811061105557611055612860565b602002015190529050600061106b8a8486611aab565b905060005b60028110156110c85789816002811061108b5761108b612860565b60200201518382600281106110a2576110a2612860565b602002018181516110b391906127b8565b905250806110c0816128a7565b915050611070565b5060006110d68b8487611aab565b905060005b600281101561079c576000838683600281106110f9576110f9612860565b602002015161110890856127cf565b6111129190612825565b9050600085836002811061112857611128612860565b602002015182111561115c5785836002811061114657611146612860565b602002015161115590836127b8565b9050611181565b8186846002811061116f5761116f612860565b602002015161117e91906127b8565b90505b60006402540be400611193838d6127cf565b61119d9190612825565b90506402540be4006111af8b836127cf565b6111b99190612825565b8d85600281106111cb576111cb612860565b6020020152508291506111df9050816128a7565b9150506110db565b60008173ffffffffffffffffffffffffffffffffffffffff1663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611234573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061090891906128e0565b611260612505565b61126983610b6d565b905060005b60028110156112eb57670de0b6b3a764000083826002811061129257611292612860565b60200201518383600281106112a9576112a9612860565b60200201516112b891906127cf565b6112c29190612825565b8282600281106112d4576112d4612860565b6020020152806112e3816128a7565b91505061126e565b5092915050565b6000826112fd612505565b600061130b600160026127b8565b6113169060046127cf565b60028473ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611363573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611387919061280c565b61139191906127cf565b61139b9190612825565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061140e919061280c565b9050600061141b886111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611465573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611489919061280c565b90506000806114978a610d0b565b905082156114ad576114aa8a8286611aab565b91505b60006040518060400160405280836000600281106114cd576114cd612860565b60200201518152602001836001600281106114ea576114ea612860565b60200201519052905060005b600281101561160b57846115aa5760008b826002811061151857611518612860565b6020020151116115aa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f6960448201527f6e7300000000000000000000000000000000000000000000000000000000000060648201526084016105a2565b8a81600281106115bc576115bc612860565b60200201518382600281106115d3576115d3612860565b60200201516115e2919061288f565b8282600281106115f4576115f4612860565b602002015280611603816128a7565b9150506114f6565b5060006116198c8388611aab565b9050838111611684576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e20443000000000000060448201526064016105a2565b8085156117c75760005b60028110156117b8576000868683600281106116ac576116ac612860565b60200201516116bb90866127cf565b6116c59190612825565b905060008583600281106116db576116db612860565b602002015182111561170f578583600281106116f9576116f9612860565b602002015161170890836127b8565b9050611734565b8186846002811061172257611722612860565b602002015161173191906127b8565b90505b6402540be400611744828d6127cf565b61174e9190612825565b8c846002811061176057611760612860565b60200201528b836002811061177757611777612860565b602002015186846002811061178e5761178e612860565b6020020181815161179f91906127b8565b9052508291506117b09050816128a7565b91505061168e565b506117c48d8489611aab565b90505b6000866117d55750816117f7565b856117e081846127b8565b6117ea90896127cf565b6117f49190612825565b90505b9d9c50505050505050505050505050565b6000858161181582610d0b565b905060006118238983611258565b905060008373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611872573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611896919061280c565b6118a5906402540be4006127b8565b6118b46402540be400896127cf565b6118be9190612825565b90508288600281106118d2576118d2612860565b6020020151811061193f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f4578636573732062616c616e636500000000000000000000000000000000000060448201526064016105a2565b600061194a8b610b6d565b90506000670de0b6b3a7640000828b6002811061196957611969612860565b602002015161197890856127cf565b6119829190612825565b848b6002811061199457611994612860565b60200201516119a391906127b8565b905060006119b48d8c8e8589611ac8565b90506000858d600281106119ca576119ca612860565b60200201516119d990836127b8565b9050838d600281106119ed576119ed612860565b6020020151611a04670de0b6b3a7640000836127cf565b611a0e9190612825565b611a1990600161288f565b9050898111156117f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f45786368616e676520726573756c74656420696e20666577657220636f696e7360448201527f207468616e20657870656374656400000000000000000000000000000000000060648201526084016105a2565b6000611ac0611aba8585611258565b83612085565b949350505050565b60008086905060008173ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3f919061280c565b90506000611b4d8583612085565b905080600080611b5e6002866127cf565b90506000805b6002811015611be7578c811415611b7d578a9150611ba7565b8b8114611ba257898160028110611b9657611b96612860565b60200201519150611ba7565b611bd5565b611bb1828561288f565b9350611bbe6002836127cf565b611bc887876127cf565b611bd29190612825565b94505b80611bdf816128a7565b915050611b64565b50611bf36002836127cf565b611bfd86866127cf565b611c079190612825565b93506000611c158387612825565b611c1f908561288f565b9050600086815b60ff811015611cc1578192508884836002611c4191906127cf565b611c4b919061288f565b611c5591906127b8565b88611c6084806127cf565b611c6a919061288f565b611c749190612825565b915082821115611c99576001611c8a84846127b8565b11611c9457611cc1565b611caf565b6001611ca583856127b8565b11611caf57611cc1565b80611cb9816128a7565b915050611c26565b509f9e505050505050505050505050505050565b600080600085905060008173ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d4e919061280c565b90506000611d5e600160026127b8565b611d699060046127cf565b60028473ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611db6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dda919061280c565b611de491906127cf565b611dee9190612825565b90506000611dfb89610c3f565b90506000611e088a61221b565b90506000611e168286612085565b90506000611e238c6111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e91919061280c565b611e9b838d6127cf565b611ea59190612825565b611eaf90836127b8565b9050826000611ec0888d8486612327565b90506000868d60028110611ed657611ed6612860565b602002015182878f60028110611eee57611eee612860565b6020020151611efd91906127b8565b611f079190612825565b905060005b60028110156120035760008e821415611f5e578387878a8560028110611f3457611f34612860565b6020020151611f4391906127cf565b611f4d9190612825565b611f5791906127b8565b9050611faf565b8686898460028110611f7257611f72612860565b6020020151611f8191906127cf565b611f8b9190612825565b888360028110611f9d57611f9d612860565b6020020151611fac91906127b8565b90505b6402540be400611fbf828c6127cf565b611fc99190612825565b858360028110611fdb57611fdb612860565b60200201818151611fec91906127b8565b905250819050611ffb816128a7565b915050611f0c565b5060006120128a8f8688612327565b848f6002811061202457612024612860565b602002015161203391906127b8565b9050878e6002811061204757612047612860565b60200201516120576001836127b8565b6120619190612825565b90508061206e81846127b8565b9c509c505050505050505050505050935093915050565b60008060005b60028110156120c9578481600281106120a6576120a6612860565b60200201516120b5908361288f565b9150806120c1816128a7565b91505061208b565b50806120d9576000915050610908565b600081816120e86002876127cf565b905060005b60ff81101561220f578260005b600281101561214e5760028a826002811061211757612117612860565b602002015161212691906127cf565b61213086846127cf565b61213a9190612825565b915080612146816128a7565b9150506120fa565b508394508060026001612161919061288f565b61216b91906127cf565b846121776001866127b8565b61218191906127cf565b61218b919061288f565b846121976002846127cf565b6121a189876127cf565b6121ab919061288f565b6121b591906127cf565b6121bf9190612825565b9350848411156121e55760016121d586866127b8565b116121e0575061220f565b6121fc565b60016121f185876127b8565b116121fc575061220f565b5080612207816128a7565b9150506120ed565b50909695505050505050565b612223612505565b61222c82610b6d565b905060005b6002811015610c39576040517f4903b0d100000000000000000000000000000000000000000000000000000000815260048101829052670de0b6b3a76400009073ffffffffffffffffffffffffffffffffffffffff851690634903b0d190602401602060405180830381865afa1580156122af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d3919061280c565b8383600281106122e5576122e5612860565b60200201516122f491906127cf565b6122fe9190612825565b82826002811061231057612310612860565b60200201528061231f816128a7565b915050612231565b600060028410612393576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6465763a20692061626f7665204e5f434f494e5300000000000000000000000060448201526064016105a2565b816000806123a26002896127cf565b90506000805b600281101561241b578881146123d6578781600281106123ca576123ca612860565b602002015191506123db565b612409565b6123e5828561288f565b93506123f26002836127cf565b6123fc88876127cf565b6124069190612825565b94505b80612413816128a7565b9150506123a8565b506124276002836127cf565b61243187866127cf565b61243b9190612825565b935060006124498388612825565b612453908561288f565b9050600087815b60ff8110156124f557819250898483600261247591906127cf565b61247f919061288f565b61248991906127b8565b8861249484806127cf565b61249e919061288f565b6124a89190612825565b9150828211156124cd5760016124be84846127b8565b116124c8576124f5565b6124e3565b60016124d983856127b8565b116124e3576124f5565b806124ed816128a7565b91505061245a565b509b9a5050505050505050505050565b60405180604001604052806002906020820280368337509192915050565b73ffffffffffffffffffffffffffffffffffffffff8116811461254557600080fd5b50565b600082601f83011261255957600080fd5b6040516040810181811067ffffffffffffffff821117156125a3577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b80604052508060408401858111156125ba57600080fd5b845b818110156125d45780358352602092830192016125bc565b509195945050505050565b600080606083850312156125f257600080fd5b82356125fd81612523565b915061260c8460208501612548565b90509250929050565b60408101818360005b600281101561263d57815183526020928301929091019060010161261e565b50505092915050565b6000806040838503121561265957600080fd5b823561266481612523565b946020939093013593505050565b6000806000806080858703121561268857600080fd5b843561269381612523565b966020860135965060408601359560600135945092505050565b6000602082840312156126bf57600080fd5b81356126ca81612523565b9392505050565b6000806000606084860312156126e657600080fd5b83356126f181612523565b95602085013595506040909401359392505050565b600080600080600060a0868803121561271e57600080fd5b853561272981612523565b97602087013597506040870135966060810135965060800135945092505050565b60008060006080848603121561275f57600080fd5b833561276a81612523565b92506127798560208601612548565b9150606084013590509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156127ca576127ca612789565b500390565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561280757612807612789565b500290565b60006020828403121561281e57600080fd5b5051919050565b60008261285b577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082198211156128a2576128a2612789565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156128d9576128d9612789565b5060010190565b6000602082840312156128f257600080fd5b81516126ca8161252356fea164736f6c634300080a000a
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106101005760003560e01c80636c92118e11610097578063b6f03ac111610066578063b6f03ac114610238578063ca4bc7141461024b578063d73792a91461025e578063f74d92e71461026a57600080fd5b80636c92118e146101cb5780636d46a1db146101de578063aaf5eb6814610216578063ae6452b41461022557600080fd5b8063279a07cc116100d3578063279a07cc1461017c57806327e235e31461018f57806329357750146101a25780635779dcd8146101b857600080fd5b80630d098fa91461010557806315a4d7fe1461012e5780631f38b0c7146101415780632494acbd14610169575b600080fd5b6101186101133660046125df565b61027d565b6040516101259190612615565b60405180910390f35b61011861013c366004612646565b6107ab565b61015461014f366004612672565b61090e565b60408051928352602083019190915201610125565b6101186101773660046126ad565b610b6d565b61011861018a3660046126ad565b610c3f565b61011861019d3660046126ad565b610d0b565b6101aa600281565b604051908152602001610125565b6101aa6101c63660046126d1565b610dd7565b6101186101d93660046125df565b610e7c565b6101f16101ec3660046126ad565b6111e7565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610125565b6101aa670de0b6b3a764000081565b6101186102333660046125df565b611258565b6101aa6102463660046125df565b6112f2565b6101aa610259366004612706565b611808565b6101aa6402540be40081565b6101aa61027836600461274a565b611aab565b610285612505565b826000610294600160026127b8565b61029f9060046127cf565b60028373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610310919061280c565b61031a91906127cf565b6103249190612825565b905060008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610373573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610397919061280c565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061040a919061280c565b90506000610417886111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610461573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610485919061280c565b90506000806104938a610d0b565b905082156104a9576104a68a8286611aab565b91505b60006040518060400160405280836000600281106104c9576104c9612860565b60200201518152602001836001600281106104e6576104e6612860565b60200201519052905060005b600281101561060c57846105ab5760008b826002811061051457610514612860565b6020020151116105ab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f6960448201527f6e7300000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b8a81600281106105bd576105bd612860565b60200201518382600281106105d4576105d4612860565b60200201516105e3919061288f565b8282600281106105f5576105f5612860565b602002015280610604816128a7565b9150506104f2565b50600061061a8c8388611aab565b9050838111610685576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e20443000000000000060448201526064016105a2565b841561079c5760005b600281101561079a576000858583600281106106ac576106ac612860565b60200201516106bb90856127cf565b6106c59190612825565b905060008483600281106106db576106db612860565b602002015182111561070f578483600281106106f9576106f9612860565b602002015161070890836127b8565b9050610734565b8185846002811061072257610722612860565b602002015161073191906127b8565b90505b60006402540be400610746838e6127cf565b6107509190612825565b90506402540be4006107628c836127cf565b61076c9190612825565b8e856002811061077e5761077e612860565b6020020152508291506107929050816128a7565b91505061068e565b505b50505050505050505092915050565b6107b3612505565b60006107be846111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610808573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061082c919061280c565b9050610836612505565b60005b600281101561090357600083868873ffffffffffffffffffffffffffffffffffffffff16634903b0d1856040518263ffffffff1660e01b815260040161088191815260200190565b602060405180830381865afa15801561089e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108c2919061280c565b6108cc91906127cf565b6108d69190612825565b9050808383600281106108eb576108eb612860565b602002015250806108fb816128a7565b915050610839565b509150505b92915050565b600080858161091c82610d0b565b9050600061092a8983611258565b905060006109378a610b6d565b90506000670de0b6b3a7640000828b6002811061095657610956612860565b6020020151610965908a6127cf565b61096f9190612825565b838b6002811061098157610981612860565b6020020151610990919061288f565b905060006109a18c8c8c8588611ac8565b90506000600182868d600281106109ba576109ba612860565b60200201516109c991906127b8565b6109d391906127b8565b905060006402540be4008873ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a28573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4c919061280c565b610a5690846127cf565b610a609190612825565b905060006402540be4008973ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ab5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad9919061280c565b610ae390846127cf565b610aed9190612825565b9050858d60028110610b0157610b01612860565b6020020151610b18670de0b6b3a7640000846127cf565b610b229190612825565b9150858d60028110610b3657610b36612860565b6020020151610b4d670de0b6b3a7640000836127cf565b610b579190612825565b919f919e50909c50505050505050505050505050565b610b75612505565b60005b6002811015610c39576040517f62203d740000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff8416906362203d7490602401602060405180830381865afa158015610bec573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c10919061280c565b828260028110610c2257610c22612860565b602002015280610c31816128a7565b915050610b78565b50919050565b610c47612505565b60005b6002811015610c39576040517f7dafa3640000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff841690637dafa36490602401602060405180830381865afa158015610cbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ce2919061280c565b828260028110610cf457610cf4612860565b602002015280610d03816128a7565b915050610c4a565b610d13612505565b60005b6002811015610c39576040517f4903b0d10000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff841690634903b0d190602401602060405180830381865afa158015610d8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dae919061280c565b828260028110610dc057610dc0612860565b602002015280610dcf816128a7565b915050610d16565b60008381610de6828686611cd5565b9150506402540be4008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5e919061280c565b610e6890836127cf565b610e729190612825565b9695505050505050565b610e84612505565b826000610e93600160026127b8565b610e9e9060046127cf565b60028373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0f919061280c565b610f1991906127cf565b610f239190612825565b905060008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f96919061280c565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fe5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611009919061280c565b9050600061101688610d0b565b9050600060405180604001604052808360006002811061103857611038612860565b602002015181526020018360016002811061105557611055612860565b602002015190529050600061106b8a8486611aab565b905060005b60028110156110c85789816002811061108b5761108b612860565b60200201518382600281106110a2576110a2612860565b602002018181516110b391906127b8565b905250806110c0816128a7565b915050611070565b5060006110d68b8487611aab565b905060005b600281101561079c576000838683600281106110f9576110f9612860565b602002015161110890856127cf565b6111129190612825565b9050600085836002811061112857611128612860565b602002015182111561115c5785836002811061114657611146612860565b602002015161115590836127b8565b9050611181565b8186846002811061116f5761116f612860565b602002015161117e91906127b8565b90505b60006402540be400611193838d6127cf565b61119d9190612825565b90506402540be4006111af8b836127cf565b6111b99190612825565b8d85600281106111cb576111cb612860565b6020020152508291506111df9050816128a7565b9150506110db565b60008173ffffffffffffffffffffffffffffffffffffffff1663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611234573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061090891906128e0565b611260612505565b61126983610b6d565b905060005b60028110156112eb57670de0b6b3a764000083826002811061129257611292612860565b60200201518383600281106112a9576112a9612860565b60200201516112b891906127cf565b6112c29190612825565b8282600281106112d4576112d4612860565b6020020152806112e3816128a7565b91505061126e565b5092915050565b6000826112fd612505565b600061130b600160026127b8565b6113169060046127cf565b60028473ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611363573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611387919061280c565b61139191906127cf565b61139b9190612825565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061140e919061280c565b9050600061141b886111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611465573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611489919061280c565b90506000806114978a610d0b565b905082156114ad576114aa8a8286611aab565b91505b60006040518060400160405280836000600281106114cd576114cd612860565b60200201518152602001836001600281106114ea576114ea612860565b60200201519052905060005b600281101561160b57846115aa5760008b826002811061151857611518612860565b6020020151116115aa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f6960448201527f6e7300000000000000000000000000000000000000000000000000000000000060648201526084016105a2565b8a81600281106115bc576115bc612860565b60200201518382600281106115d3576115d3612860565b60200201516115e2919061288f565b8282600281106115f4576115f4612860565b602002015280611603816128a7565b9150506114f6565b5060006116198c8388611aab565b9050838111611684576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e20443000000000000060448201526064016105a2565b8085156117c75760005b60028110156117b8576000868683600281106116ac576116ac612860565b60200201516116bb90866127cf565b6116c59190612825565b905060008583600281106116db576116db612860565b602002015182111561170f578583600281106116f9576116f9612860565b602002015161170890836127b8565b9050611734565b8186846002811061172257611722612860565b602002015161173191906127b8565b90505b6402540be400611744828d6127cf565b61174e9190612825565b8c846002811061176057611760612860565b60200201528b836002811061177757611777612860565b602002015186846002811061178e5761178e612860565b6020020181815161179f91906127b8565b9052508291506117b09050816128a7565b91505061168e565b506117c48d8489611aab565b90505b6000866117d55750816117f7565b856117e081846127b8565b6117ea90896127cf565b6117f49190612825565b90505b9d9c50505050505050505050505050565b6000858161181582610d0b565b905060006118238983611258565b905060008373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611872573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611896919061280c565b6118a5906402540be4006127b8565b6118b46402540be400896127cf565b6118be9190612825565b90508288600281106118d2576118d2612860565b6020020151811061193f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f4578636573732062616c616e636500000000000000000000000000000000000060448201526064016105a2565b600061194a8b610b6d565b90506000670de0b6b3a7640000828b6002811061196957611969612860565b602002015161197890856127cf565b6119829190612825565b848b6002811061199457611994612860565b60200201516119a391906127b8565b905060006119b48d8c8e8589611ac8565b90506000858d600281106119ca576119ca612860565b60200201516119d990836127b8565b9050838d600281106119ed576119ed612860565b6020020151611a04670de0b6b3a7640000836127cf565b611a0e9190612825565b611a1990600161288f565b9050898111156117f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f45786368616e676520726573756c74656420696e20666577657220636f696e7360448201527f207468616e20657870656374656400000000000000000000000000000000000060648201526084016105a2565b6000611ac0611aba8585611258565b83612085565b949350505050565b60008086905060008173ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b3f919061280c565b90506000611b4d8583612085565b905080600080611b5e6002866127cf565b90506000805b6002811015611be7578c811415611b7d578a9150611ba7565b8b8114611ba257898160028110611b9657611b96612860565b60200201519150611ba7565b611bd5565b611bb1828561288f565b9350611bbe6002836127cf565b611bc887876127cf565b611bd29190612825565b94505b80611bdf816128a7565b915050611b64565b50611bf36002836127cf565b611bfd86866127cf565b611c079190612825565b93506000611c158387612825565b611c1f908561288f565b9050600086815b60ff811015611cc1578192508884836002611c4191906127cf565b611c4b919061288f565b611c5591906127b8565b88611c6084806127cf565b611c6a919061288f565b611c749190612825565b915082821115611c99576001611c8a84846127b8565b11611c9457611cc1565b611caf565b6001611ca583856127b8565b11611caf57611cc1565b80611cb9816128a7565b915050611c26565b509f9e505050505050505050505050505050565b600080600085905060008173ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611d2a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d4e919061280c565b90506000611d5e600160026127b8565b611d699060046127cf565b60028473ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611db6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dda919061280c565b611de491906127cf565b611dee9190612825565b90506000611dfb89610c3f565b90506000611e088a61221b565b90506000611e168286612085565b90506000611e238c6111e7565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611e6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e91919061280c565b611e9b838d6127cf565b611ea59190612825565b611eaf90836127b8565b9050826000611ec0888d8486612327565b90506000868d60028110611ed657611ed6612860565b602002015182878f60028110611eee57611eee612860565b6020020151611efd91906127b8565b611f079190612825565b905060005b60028110156120035760008e821415611f5e578387878a8560028110611f3457611f34612860565b6020020151611f4391906127cf565b611f4d9190612825565b611f5791906127b8565b9050611faf565b8686898460028110611f7257611f72612860565b6020020151611f8191906127cf565b611f8b9190612825565b888360028110611f9d57611f9d612860565b6020020151611fac91906127b8565b90505b6402540be400611fbf828c6127cf565b611fc99190612825565b858360028110611fdb57611fdb612860565b60200201818151611fec91906127b8565b905250819050611ffb816128a7565b915050611f0c565b5060006120128a8f8688612327565b848f6002811061202457612024612860565b602002015161203391906127b8565b9050878e6002811061204757612047612860565b60200201516120576001836127b8565b6120619190612825565b90508061206e81846127b8565b9c509c505050505050505050505050935093915050565b60008060005b60028110156120c9578481600281106120a6576120a6612860565b60200201516120b5908361288f565b9150806120c1816128a7565b91505061208b565b50806120d9576000915050610908565b600081816120e86002876127cf565b905060005b60ff81101561220f578260005b600281101561214e5760028a826002811061211757612117612860565b602002015161212691906127cf565b61213086846127cf565b61213a9190612825565b915080612146816128a7565b9150506120fa565b508394508060026001612161919061288f565b61216b91906127cf565b846121776001866127b8565b61218191906127cf565b61218b919061288f565b846121976002846127cf565b6121a189876127cf565b6121ab919061288f565b6121b591906127cf565b6121bf9190612825565b9350848411156121e55760016121d586866127b8565b116121e0575061220f565b6121fc565b60016121f185876127b8565b116121fc575061220f565b5080612207816128a7565b9150506120ed565b50909695505050505050565b612223612505565b61222c82610b6d565b905060005b6002811015610c39576040517f4903b0d100000000000000000000000000000000000000000000000000000000815260048101829052670de0b6b3a76400009073ffffffffffffffffffffffffffffffffffffffff851690634903b0d190602401602060405180830381865afa1580156122af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d3919061280c565b8383600281106122e5576122e5612860565b60200201516122f491906127cf565b6122fe9190612825565b82826002811061231057612310612860565b60200201528061231f816128a7565b915050612231565b600060028410612393576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6465763a20692061626f7665204e5f434f494e5300000000000000000000000060448201526064016105a2565b816000806123a26002896127cf565b90506000805b600281101561241b578881146123d6578781600281106123ca576123ca612860565b602002015191506123db565b612409565b6123e5828561288f565b93506123f26002836127cf565b6123fc88876127cf565b6124069190612825565b94505b80612413816128a7565b9150506123a8565b506124276002836127cf565b61243187866127cf565b61243b9190612825565b935060006124498388612825565b612453908561288f565b9050600087815b60ff8110156124f557819250898483600261247591906127cf565b61247f919061288f565b61248991906127b8565b8861249484806127cf565b61249e919061288f565b6124a89190612825565b9150828211156124cd5760016124be84846127b8565b116124c8576124f5565b6124e3565b60016124d983856127b8565b116124e3576124f5565b806124ed816128a7565b91505061245a565b509b9a5050505050505050505050565b60405180604001604052806002906020820280368337509192915050565b73ffffffffffffffffffffffffffffffffffffffff8116811461254557600080fd5b50565b600082601f83011261255957600080fd5b6040516040810181811067ffffffffffffffff821117156125a3577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b80604052508060408401858111156125ba57600080fd5b845b818110156125d45780358352602092830192016125bc565b509195945050505050565b600080606083850312156125f257600080fd5b82356125fd81612523565b915061260c8460208501612548565b90509250929050565b60408101818360005b600281101561263d57815183526020928301929091019060010161261e565b50505092915050565b6000806040838503121561265957600080fd5b823561266481612523565b946020939093013593505050565b6000806000806080858703121561268857600080fd5b843561269381612523565b966020860135965060408601359560600135945092505050565b6000602082840312156126bf57600080fd5b81356126ca81612523565b9392505050565b6000806000606084860312156126e657600080fd5b83356126f181612523565b95602085013595506040909401359392505050565b600080600080600060a0868803121561271e57600080fd5b853561272981612523565b97602087013597506040870135966060810135965060800135945092505050565b60008060006080848603121561275f57600080fd5b833561276a81612523565b92506127798560208601612548565b9150606084013590509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156127ca576127ca612789565b500390565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561280757612807612789565b500290565b60006020828403121561281e57600080fd5b5051919050565b60008261285b577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082198211156128a2576128a2612789565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156128d9576128d9612789565b5060010190565b6000602082840312156128f257600080fd5b81516126ca8161252356fea164736f6c634300080a000a