Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- NineinchStableSwapInfo
- Optimization enabled
- true
- Compiler version
- v0.8.10+commit.fc410830
- Optimization runs
- 1000000
- EVM Version
- istanbul
- Verified at
- 2024-04-09T09:12:30.058430Z
contracts/NineinchStableSwapInfo.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/INineinchStableSwap.sol";
contract NineinchStableSwapInfo {
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;
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;
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;
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_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;
}
}
@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);
}
}
}
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);
}
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":"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
0x608060405234801561001057600080fd5b50612501806100206000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80635779dcd811610097578063ae6452b411610066578063ae6452b41461021a578063b6f03ac11461022d578063d73792a914610240578063f74d92e71461024c57600080fd5b80635779dcd8146101ad5780636c92118e146101c05780636d46a1db146101d3578063aaf5eb681461020b57600080fd5b80632494acbd116100d35780632494acbd1461015e578063279a07cc1461017157806327e235e314610184578063293577501461019757600080fd5b80630d098fa9146100fa57806315a4d7fe146101235780631f38b0c714610136575b600080fd5b61010d61010836600461221a565b61025f565b60405161011a9190612250565b60405180910390f35b61010d610131366004612281565b610748565b6101496101443660046122ad565b6108ab565b6040805192835260208301919091520161011a565b61010d61016c3660046122e8565b610ad5565b61010d61017f3660046122e8565b610ba7565b61010d6101923660046122e8565b610c73565b61019f600281565b60405190815260200161011a565b61019f6101bb36600461230c565b610d3f565b61010d6101ce36600461221a565b610de4565b6101e66101e13660046122e8565b61110a565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161011a565b61019f670de0b6b3a764000081565b61010d61022836600461221a565b61117b565b61019f61023b36600461221a565b611215565b61019f6402540be40081565b61019f61025a366004612341565b6116e6565b610267612140565b826000610276600160026123af565b6102819060046123c6565b60028373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102f29190612403565b6102fc91906123c6565b610306919061241c565b905060008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610355573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103799190612403565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ec9190612403565b905060006103f98861110a565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610443573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104679190612403565b90506000806104758a610c73565b9050821561048b576104888a82866116e6565b91505b8060005b60028110156105a957846105485760008b82600281106104b1576104b1612457565b602002015111610548576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f6960448201527f6e7300000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b8a816002811061055a5761055a612457565b602002015183826002811061057157610571612457565b60200201516105809190612486565b82826002811061059257610592612457565b6020020152806105a18161249e565b91505061048f565b5060006105b78c83886116e6565b9050838111610622576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e204430000000000000604482015260640161053f565b84156107395760005b60028110156107375760008585836002811061064957610649612457565b602002015161065890856123c6565b610662919061241c565b9050600084836002811061067857610678612457565b60200201518211156106ac5784836002811061069657610696612457565b60200201516106a590836123af565b90506106d1565b818584600281106106bf576106bf612457565b60200201516106ce91906123af565b90505b60006402540be4006106e3838e6123c6565b6106ed919061241c565b90506402540be4006106ff8c836123c6565b610709919061241c565b8e856002811061071b5761071b612457565b60200201525082915061072f90508161249e565b91505061062b565b505b50505050505050505092915050565b610750612140565b600061075b8461110a565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c99190612403565b90506107d3612140565b60005b60028110156108a057600083868873ffffffffffffffffffffffffffffffffffffffff16634903b0d1856040518263ffffffff1660e01b815260040161081e91815260200190565b602060405180830381865afa15801561083b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085f9190612403565b61086991906123c6565b610873919061241c565b90508083836002811061088857610888612457565b602002015250806108988161249e565b9150506107d6565b509150505b92915050565b60008085816108b982610c73565b905060006108c7898361117b565b905060006108d48a610ad5565b90506000670de0b6b3a7640000828b600281106108f3576108f3612457565b6020020151610902908a6123c6565b61090c919061241c565b838b6002811061091e5761091e612457565b602002015161092d9190612486565b9050600061093e8c8c8c8588611703565b90506000600182868d6002811061095757610957612457565b602002015161096691906123af565b61097091906123af565b905060006402540be4008873ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109e99190612403565b6109f390846123c6565b6109fd919061241c565b905060006402540be4008973ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a769190612403565b610a8090846123c6565b610a8a919061241c565b9050858d60028110610a9e57610a9e612457565b6020020151610ab5670de0b6b3a7640000836123c6565b610abf919061241c565b919f919e50909c50505050505050505050505050565b610add612140565b60005b6002811015610ba1576040517f62203d740000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff8416906362203d7490602401602060405180830381865afa158015610b54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b789190612403565b828260028110610b8a57610b8a612457565b602002015280610b998161249e565b915050610ae0565b50919050565b610baf612140565b60005b6002811015610ba1576040517f7dafa3640000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff841690637dafa36490602401602060405180830381865afa158015610c26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4a9190612403565b828260028110610c5c57610c5c612457565b602002015280610c6b8161249e565b915050610bb2565b610c7b612140565b60005b6002811015610ba1576040517f4903b0d10000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff841690634903b0d190602401602060405180830381865afa158015610cf2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d169190612403565b828260028110610d2857610d28612457565b602002015280610d378161249e565b915050610c7e565b60008381610d4e828686611910565b9150506402540be4008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610da2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc69190612403565b610dd090836123c6565b610dda919061241c565b9695505050505050565b610dec612140565b826000610dfb600160026123af565b610e069060046123c6565b60028373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e779190612403565b610e8191906123c6565b610e8b919061241c565b905060008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610efe9190612403565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f719190612403565b90506000610f7e88610c73565b9050806000610f8e8a83866116e6565b905060005b6002811015610feb57898160028110610fae57610fae612457565b6020020151838260028110610fc557610fc5612457565b60200201818151610fd691906123af565b90525080610fe38161249e565b915050610f93565b506000610ff98b84876116e6565b905060005b60028110156107395760008386836002811061101c5761101c612457565b602002015161102b90856123c6565b611035919061241c565b9050600085836002811061104b5761104b612457565b602002015182111561107f5785836002811061106957611069612457565b602002015161107890836123af565b90506110a4565b8186846002811061109257611092612457565b60200201516110a191906123af565b90505b60006402540be4006110b6838d6123c6565b6110c0919061241c565b90506402540be4006110d28b836123c6565b6110dc919061241c565b8d85600281106110ee576110ee612457565b60200201525082915061110290508161249e565b915050610ffe565b60008173ffffffffffffffffffffffffffffffffffffffff1663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611157573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a591906124d7565b611183612140565b61118c83610ad5565b905060005b600281101561120e57670de0b6b3a76400008382600281106111b5576111b5612457565b60200201518383600281106111cc576111cc612457565b60200201516111db91906123c6565b6111e5919061241c565b8282600281106111f7576111f7612457565b6020020152806112068161249e565b915050611191565b5092915050565b600082611220612140565b600061122e600160026123af565b6112399060046123c6565b60028473ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611286573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112aa9190612403565b6112b491906123c6565b6112be919061241c565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113319190612403565b9050600061133e8861110a565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611388573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ac9190612403565b90506000806113ba8a610c73565b905082156113d0576113cd8a82866116e6565b91505b8060005b60028110156114e957846114885760008b82600281106113f6576113f6612457565b602002015111611488576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f6960448201527f6e73000000000000000000000000000000000000000000000000000000000000606482015260840161053f565b8a816002811061149a5761149a612457565b60200201518382600281106114b1576114b1612457565b60200201516114c09190612486565b8282600281106114d2576114d2612457565b6020020152806114e18161249e565b9150506113d4565b5060006114f78c83886116e6565b9050838111611562576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e204430000000000000604482015260640161053f565b8085156116a55760005b60028110156116965760008686836002811061158a5761158a612457565b602002015161159990866123c6565b6115a3919061241c565b905060008583600281106115b9576115b9612457565b60200201518211156115ed578583600281106115d7576115d7612457565b60200201516115e690836123af565b9050611612565b8186846002811061160057611600612457565b602002015161160f91906123af565b90505b6402540be400611622828d6123c6565b61162c919061241c565b8c846002811061163e5761163e612457565b60200201528b836002811061165557611655612457565b602002015186846002811061166c5761166c612457565b6020020181815161167d91906123af565b90525082915061168e90508161249e565b91505061156c565b506116a28d84896116e6565b90505b6000866116b35750816116d5565b856116be81846123af565b6116c890896123c6565b6116d2919061241c565b90505b9d9c50505050505050505050505050565b60006116fb6116f5858561117b565b83611cc0565b949350505050565b60008086905060008173ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611756573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061177a9190612403565b905060006117888583611cc0565b9050806000806117996002866123c6565b90506000805b6002811015611822578c8114156117b8578a91506117e2565b8b81146117dd578981600281106117d1576117d1612457565b602002015191506117e2565b611810565b6117ec8285612486565b93506117f96002836123c6565b61180387876123c6565b61180d919061241c565b94505b8061181a8161249e565b91505061179f565b5061182e6002836123c6565b61183886866123c6565b611842919061241c565b93506000611850838761241c565b61185a9085612486565b9050600086815b60ff8110156118fc57819250888483600261187c91906123c6565b6118869190612486565b61189091906123af565b8861189b84806123c6565b6118a59190612486565b6118af919061241c565b9150828211156118d45760016118c584846123af565b116118cf576118fc565b6118ea565b60016118e083856123af565b116118ea576118fc565b806118f48161249e565b915050611861565b509f9e505050505050505050505050505050565b600080600085905060008173ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611965573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119899190612403565b90506000611999600160026123af565b6119a49060046123c6565b60028473ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a159190612403565b611a1f91906123c6565b611a29919061241c565b90506000611a3689610ba7565b90506000611a438a611e56565b90506000611a518286611cc0565b90506000611a5e8c61110a565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611aa8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611acc9190612403565b611ad6838d6123c6565b611ae0919061241c565b611aea90836123af565b9050826000611afb888d8486611f62565b90506000868d60028110611b1157611b11612457565b602002015182878f60028110611b2957611b29612457565b6020020151611b3891906123af565b611b42919061241c565b905060005b6002811015611c3e5760008e821415611b99578387878a8560028110611b6f57611b6f612457565b6020020151611b7e91906123c6565b611b88919061241c565b611b9291906123af565b9050611bea565b8686898460028110611bad57611bad612457565b6020020151611bbc91906123c6565b611bc6919061241c565b888360028110611bd857611bd8612457565b6020020151611be791906123af565b90505b6402540be400611bfa828c6123c6565b611c04919061241c565b858360028110611c1657611c16612457565b60200201818151611c2791906123af565b905250819050611c368161249e565b915050611b47565b506000611c4d8a8f8688611f62565b848f60028110611c5f57611c5f612457565b6020020151611c6e91906123af565b9050878e60028110611c8257611c82612457565b6020020151611c926001836123af565b611c9c919061241c565b905080611ca981846123af565b9c509c505050505050505050505050935093915050565b60008060005b6002811015611d0457848160028110611ce157611ce1612457565b6020020151611cf09083612486565b915080611cfc8161249e565b915050611cc6565b5080611d145760009150506108a5565b60008181611d236002876123c6565b905060005b60ff811015611e4a578260005b6002811015611d895760028a8260028110611d5257611d52612457565b6020020151611d6191906123c6565b611d6b86846123c6565b611d75919061241c565b915080611d818161249e565b915050611d35565b508394508060026001611d9c9190612486565b611da691906123c6565b84611db26001866123af565b611dbc91906123c6565b611dc69190612486565b84611dd26002846123c6565b611ddc89876123c6565b611de69190612486565b611df091906123c6565b611dfa919061241c565b935084841115611e20576001611e1086866123af565b11611e1b5750611e4a565b611e37565b6001611e2c85876123af565b11611e375750611e4a565b5080611e428161249e565b915050611d28565b50909695505050505050565b611e5e612140565b611e6782610ad5565b905060005b6002811015610ba1576040517f4903b0d100000000000000000000000000000000000000000000000000000000815260048101829052670de0b6b3a76400009073ffffffffffffffffffffffffffffffffffffffff851690634903b0d190602401602060405180830381865afa158015611eea573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f0e9190612403565b838360028110611f2057611f20612457565b6020020151611f2f91906123c6565b611f39919061241c565b828260028110611f4b57611f4b612457565b602002015280611f5a8161249e565b915050611e6c565b600060028410611fce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6465763a20692061626f7665204e5f434f494e53000000000000000000000000604482015260640161053f565b81600080611fdd6002896123c6565b90506000805b6002811015612056578881146120115787816002811061200557612005612457565b60200201519150612016565b612044565b6120208285612486565b935061202d6002836123c6565b61203788876123c6565b612041919061241c565b94505b8061204e8161249e565b915050611fe3565b506120626002836123c6565b61206c87866123c6565b612076919061241c565b93506000612084838861241c565b61208e9085612486565b9050600087815b60ff8110156121305781925089848360026120b091906123c6565b6120ba9190612486565b6120c491906123af565b886120cf84806123c6565b6120d99190612486565b6120e3919061241c565b9150828211156121085760016120f984846123af565b1161210357612130565b61211e565b600161211483856123af565b1161211e57612130565b806121288161249e565b915050612095565b509b9a5050505050505050505050565b60405180604001604052806002906020820280368337509192915050565b73ffffffffffffffffffffffffffffffffffffffff8116811461218057600080fd5b50565b600082601f83011261219457600080fd5b6040516040810181811067ffffffffffffffff821117156121de577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b80604052508060408401858111156121f557600080fd5b845b8181101561220f5780358352602092830192016121f7565b509195945050505050565b6000806060838503121561222d57600080fd5b82356122388161215e565b91506122478460208501612183565b90509250929050565b60408101818360005b6002811015612278578151835260209283019290910190600101612259565b50505092915050565b6000806040838503121561229457600080fd5b823561229f8161215e565b946020939093013593505050565b600080600080608085870312156122c357600080fd5b84356122ce8161215e565b966020860135965060408601359560600135945092505050565b6000602082840312156122fa57600080fd5b81356123058161215e565b9392505050565b60008060006060848603121561232157600080fd5b833561232c8161215e565b95602085013595506040909401359392505050565b60008060006080848603121561235657600080fd5b83356123618161215e565b92506123708560208601612183565b9150606084013590509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156123c1576123c1612380565b500390565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156123fe576123fe612380565b500290565b60006020828403121561241557600080fd5b5051919050565b600082612452577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000821982111561249957612499612380565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156124d0576124d0612380565b5060010190565b6000602082840312156124e957600080fd5b81516123058161215e56fea164736f6c634300080a000a
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106100f55760003560e01c80635779dcd811610097578063ae6452b411610066578063ae6452b41461021a578063b6f03ac11461022d578063d73792a914610240578063f74d92e71461024c57600080fd5b80635779dcd8146101ad5780636c92118e146101c05780636d46a1db146101d3578063aaf5eb681461020b57600080fd5b80632494acbd116100d35780632494acbd1461015e578063279a07cc1461017157806327e235e314610184578063293577501461019757600080fd5b80630d098fa9146100fa57806315a4d7fe146101235780631f38b0c714610136575b600080fd5b61010d61010836600461221a565b61025f565b60405161011a9190612250565b60405180910390f35b61010d610131366004612281565b610748565b6101496101443660046122ad565b6108ab565b6040805192835260208301919091520161011a565b61010d61016c3660046122e8565b610ad5565b61010d61017f3660046122e8565b610ba7565b61010d6101923660046122e8565b610c73565b61019f600281565b60405190815260200161011a565b61019f6101bb36600461230c565b610d3f565b61010d6101ce36600461221a565b610de4565b6101e66101e13660046122e8565b61110a565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161011a565b61019f670de0b6b3a764000081565b61010d61022836600461221a565b61117b565b61019f61023b36600461221a565b611215565b61019f6402540be40081565b61019f61025a366004612341565b6116e6565b610267612140565b826000610276600160026123af565b6102819060046123c6565b60028373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156102ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102f29190612403565b6102fc91906123c6565b610306919061241c565b905060008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610355573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103799190612403565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ec9190612403565b905060006103f98861110a565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610443573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104679190612403565b90506000806104758a610c73565b9050821561048b576104888a82866116e6565b91505b8060005b60028110156105a957846105485760008b82600281106104b1576104b1612457565b602002015111610548576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f6960448201527f6e7300000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b8a816002811061055a5761055a612457565b602002015183826002811061057157610571612457565b60200201516105809190612486565b82826002811061059257610592612457565b6020020152806105a18161249e565b91505061048f565b5060006105b78c83886116e6565b9050838111610622576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e204430000000000000604482015260640161053f565b84156107395760005b60028110156107375760008585836002811061064957610649612457565b602002015161065890856123c6565b610662919061241c565b9050600084836002811061067857610678612457565b60200201518211156106ac5784836002811061069657610696612457565b60200201516106a590836123af565b90506106d1565b818584600281106106bf576106bf612457565b60200201516106ce91906123af565b90505b60006402540be4006106e3838e6123c6565b6106ed919061241c565b90506402540be4006106ff8c836123c6565b610709919061241c565b8e856002811061071b5761071b612457565b60200201525082915061072f90508161249e565b91505061062b565b505b50505050505050505092915050565b610750612140565b600061075b8461110a565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c99190612403565b90506107d3612140565b60005b60028110156108a057600083868873ffffffffffffffffffffffffffffffffffffffff16634903b0d1856040518263ffffffff1660e01b815260040161081e91815260200190565b602060405180830381865afa15801561083b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085f9190612403565b61086991906123c6565b610873919061241c565b90508083836002811061088857610888612457565b602002015250806108988161249e565b9150506107d6565b509150505b92915050565b60008085816108b982610c73565b905060006108c7898361117b565b905060006108d48a610ad5565b90506000670de0b6b3a7640000828b600281106108f3576108f3612457565b6020020151610902908a6123c6565b61090c919061241c565b838b6002811061091e5761091e612457565b602002015161092d9190612486565b9050600061093e8c8c8c8588611703565b90506000600182868d6002811061095757610957612457565b602002015161096691906123af565b61097091906123af565b905060006402540be4008873ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109e99190612403565b6109f390846123c6565b6109fd919061241c565b905060006402540be4008973ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a769190612403565b610a8090846123c6565b610a8a919061241c565b9050858d60028110610a9e57610a9e612457565b6020020151610ab5670de0b6b3a7640000836123c6565b610abf919061241c565b919f919e50909c50505050505050505050505050565b610add612140565b60005b6002811015610ba1576040517f62203d740000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff8416906362203d7490602401602060405180830381865afa158015610b54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b789190612403565b828260028110610b8a57610b8a612457565b602002015280610b998161249e565b915050610ae0565b50919050565b610baf612140565b60005b6002811015610ba1576040517f7dafa3640000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff841690637dafa36490602401602060405180830381865afa158015610c26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4a9190612403565b828260028110610c5c57610c5c612457565b602002015280610c6b8161249e565b915050610bb2565b610c7b612140565b60005b6002811015610ba1576040517f4903b0d10000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff841690634903b0d190602401602060405180830381865afa158015610cf2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d169190612403565b828260028110610d2857610d28612457565b602002015280610d378161249e565b915050610c7e565b60008381610d4e828686611910565b9150506402540be4008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610da2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc69190612403565b610dd090836123c6565b610dda919061241c565b9695505050505050565b610dec612140565b826000610dfb600160026123af565b610e069060046123c6565b60028373ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e779190612403565b610e8191906123c6565b610e8b919061241c565b905060008273ffffffffffffffffffffffffffffffffffffffff1663fee3f7f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015610eda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610efe9190612403565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f719190612403565b90506000610f7e88610c73565b9050806000610f8e8a83866116e6565b905060005b6002811015610feb57898160028110610fae57610fae612457565b6020020151838260028110610fc557610fc5612457565b60200201818151610fd691906123af565b90525080610fe38161249e565b915050610f93565b506000610ff98b84876116e6565b905060005b60028110156107395760008386836002811061101c5761101c612457565b602002015161102b90856123c6565b611035919061241c565b9050600085836002811061104b5761104b612457565b602002015182111561107f5785836002811061106957611069612457565b602002015161107890836123af565b90506110a4565b8186846002811061109257611092612457565b60200201516110a191906123af565b90505b60006402540be4006110b6838d6123c6565b6110c0919061241c565b90506402540be4006110d28b836123c6565b6110dc919061241c565b8d85600281106110ee576110ee612457565b60200201525082915061110290508161249e565b915050610ffe565b60008173ffffffffffffffffffffffffffffffffffffffff1663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611157573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a591906124d7565b611183612140565b61118c83610ad5565b905060005b600281101561120e57670de0b6b3a76400008382600281106111b5576111b5612457565b60200201518383600281106111cc576111cc612457565b60200201516111db91906123c6565b6111e5919061241c565b8282600281106111f7576111f7612457565b6020020152806112068161249e565b915050611191565b5092915050565b600082611220612140565b600061122e600160026123af565b6112399060046123c6565b60028473ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa158015611286573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112aa9190612403565b6112b491906123c6565b6112be919061241c565b905060008373ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561130d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113319190612403565b9050600061133e8861110a565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611388573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ac9190612403565b90506000806113ba8a610c73565b905082156113d0576113cd8a82866116e6565b91505b8060005b60028110156114e957846114885760008b82600281106113f6576113f6612457565b602002015111611488576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f496e697469616c206465706f73697420726571756972657320616c6c20636f6960448201527f6e73000000000000000000000000000000000000000000000000000000000000606482015260840161053f565b8a816002811061149a5761149a612457565b60200201518382600281106114b1576114b1612457565b60200201516114c09190612486565b8282600281106114d2576114d2612457565b6020020152806114e18161249e565b9150506113d4565b5060006114f78c83886116e6565b9050838111611562576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4431206d7573742062652067726561746572207468616e204430000000000000604482015260640161053f565b8085156116a55760005b60028110156116965760008686836002811061158a5761158a612457565b602002015161159990866123c6565b6115a3919061241c565b905060008583600281106115b9576115b9612457565b60200201518211156115ed578583600281106115d7576115d7612457565b60200201516115e690836123af565b9050611612565b8186846002811061160057611600612457565b602002015161160f91906123af565b90505b6402540be400611622828d6123c6565b61162c919061241c565b8c846002811061163e5761163e612457565b60200201528b836002811061165557611655612457565b602002015186846002811061166c5761166c612457565b6020020181815161167d91906123af565b90525082915061168e90508161249e565b91505061156c565b506116a28d84896116e6565b90505b6000866116b35750816116d5565b856116be81846123af565b6116c890896123c6565b6116d2919061241c565b90505b9d9c50505050505050505050505050565b60006116fb6116f5858561117b565b83611cc0565b949350505050565b60008086905060008173ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611756573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061177a9190612403565b905060006117888583611cc0565b9050806000806117996002866123c6565b90506000805b6002811015611822578c8114156117b8578a91506117e2565b8b81146117dd578981600281106117d1576117d1612457565b602002015191506117e2565b611810565b6117ec8285612486565b93506117f96002836123c6565b61180387876123c6565b61180d919061241c565b94505b8061181a8161249e565b91505061179f565b5061182e6002836123c6565b61183886866123c6565b611842919061241c565b93506000611850838761241c565b61185a9085612486565b9050600086815b60ff8110156118fc57819250888483600261187c91906123c6565b6118869190612486565b61189091906123af565b8861189b84806123c6565b6118a59190612486565b6118af919061241c565b9150828211156118d45760016118c584846123af565b116118cf576118fc565b6118ea565b60016118e083856123af565b116118ea576118fc565b806118f48161249e565b915050611861565b509f9e505050505050505050505050505050565b600080600085905060008173ffffffffffffffffffffffffffffffffffffffff1663f446c1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611965573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119899190612403565b90506000611999600160026123af565b6119a49060046123c6565b60028473ffffffffffffffffffffffffffffffffffffffff1663ddca3f436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a159190612403565b611a1f91906123c6565b611a29919061241c565b90506000611a3689610ba7565b90506000611a438a611e56565b90506000611a518286611cc0565b90506000611a5e8c61110a565b73ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611aa8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611acc9190612403565b611ad6838d6123c6565b611ae0919061241c565b611aea90836123af565b9050826000611afb888d8486611f62565b90506000868d60028110611b1157611b11612457565b602002015182878f60028110611b2957611b29612457565b6020020151611b3891906123af565b611b42919061241c565b905060005b6002811015611c3e5760008e821415611b99578387878a8560028110611b6f57611b6f612457565b6020020151611b7e91906123c6565b611b88919061241c565b611b9291906123af565b9050611bea565b8686898460028110611bad57611bad612457565b6020020151611bbc91906123c6565b611bc6919061241c565b888360028110611bd857611bd8612457565b6020020151611be791906123af565b90505b6402540be400611bfa828c6123c6565b611c04919061241c565b858360028110611c1657611c16612457565b60200201818151611c2791906123af565b905250819050611c368161249e565b915050611b47565b506000611c4d8a8f8688611f62565b848f60028110611c5f57611c5f612457565b6020020151611c6e91906123af565b9050878e60028110611c8257611c82612457565b6020020151611c926001836123af565b611c9c919061241c565b905080611ca981846123af565b9c509c505050505050505050505050935093915050565b60008060005b6002811015611d0457848160028110611ce157611ce1612457565b6020020151611cf09083612486565b915080611cfc8161249e565b915050611cc6565b5080611d145760009150506108a5565b60008181611d236002876123c6565b905060005b60ff811015611e4a578260005b6002811015611d895760028a8260028110611d5257611d52612457565b6020020151611d6191906123c6565b611d6b86846123c6565b611d75919061241c565b915080611d818161249e565b915050611d35565b508394508060026001611d9c9190612486565b611da691906123c6565b84611db26001866123af565b611dbc91906123c6565b611dc69190612486565b84611dd26002846123c6565b611ddc89876123c6565b611de69190612486565b611df091906123c6565b611dfa919061241c565b935084841115611e20576001611e1086866123af565b11611e1b5750611e4a565b611e37565b6001611e2c85876123af565b11611e375750611e4a565b5080611e428161249e565b915050611d28565b50909695505050505050565b611e5e612140565b611e6782610ad5565b905060005b6002811015610ba1576040517f4903b0d100000000000000000000000000000000000000000000000000000000815260048101829052670de0b6b3a76400009073ffffffffffffffffffffffffffffffffffffffff851690634903b0d190602401602060405180830381865afa158015611eea573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f0e9190612403565b838360028110611f2057611f20612457565b6020020151611f2f91906123c6565b611f39919061241c565b828260028110611f4b57611f4b612457565b602002015280611f5a8161249e565b915050611e6c565b600060028410611fce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6465763a20692061626f7665204e5f434f494e53000000000000000000000000604482015260640161053f565b81600080611fdd6002896123c6565b90506000805b6002811015612056578881146120115787816002811061200557612005612457565b60200201519150612016565b612044565b6120208285612486565b935061202d6002836123c6565b61203788876123c6565b612041919061241c565b94505b8061204e8161249e565b915050611fe3565b506120626002836123c6565b61206c87866123c6565b612076919061241c565b93506000612084838861241c565b61208e9085612486565b9050600087815b60ff8110156121305781925089848360026120b091906123c6565b6120ba9190612486565b6120c491906123af565b886120cf84806123c6565b6120d99190612486565b6120e3919061241c565b9150828211156121085760016120f984846123af565b1161210357612130565b61211e565b600161211483856123af565b1161211e57612130565b806121288161249e565b915050612095565b509b9a5050505050505050505050565b60405180604001604052806002906020820280368337509192915050565b73ffffffffffffffffffffffffffffffffffffffff8116811461218057600080fd5b50565b600082601f83011261219457600080fd5b6040516040810181811067ffffffffffffffff821117156121de577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b80604052508060408401858111156121f557600080fd5b845b8181101561220f5780358352602092830192016121f7565b509195945050505050565b6000806060838503121561222d57600080fd5b82356122388161215e565b91506122478460208501612183565b90509250929050565b60408101818360005b6002811015612278578151835260209283019290910190600101612259565b50505092915050565b6000806040838503121561229457600080fd5b823561229f8161215e565b946020939093013593505050565b600080600080608085870312156122c357600080fd5b84356122ce8161215e565b966020860135965060408601359560600135945092505050565b6000602082840312156122fa57600080fd5b81356123058161215e565b9392505050565b60008060006060848603121561232157600080fd5b833561232c8161215e565b95602085013595506040909401359392505050565b60008060006080848603121561235657600080fd5b83356123618161215e565b92506123708560208601612183565b9150606084013590509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156123c1576123c1612380565b500390565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156123fe576123fe612380565b500290565b60006020828403121561241557600080fd5b5051919050565b600082612452577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000821982111561249957612499612380565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156124d0576124d0612380565b5060010190565b6000602082840312156124e957600080fd5b81516123058161215e56fea164736f6c634300080a000a