Source Code
Overview
GLMR Balance
GLMR Value
$0.00View more zero value Internal Transactions in Advanced View mode
Cross-Chain Transactions
Loading...
Loading
Contract Name:
SwapUtils
Compiler Version
v0.6.12+commit.27d51765
Optimization Enabled:
Yes with 999999 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "./AmplificationUtils.sol";
import "./LPToken.sol";
import "./MathUtils.sol";
/**
* @title SwapUtils library
* @notice A library to be used within Swap.sol. Contains functions responsible for custody and AMM functionalities.
* @dev Contracts relying on this library must initialize SwapUtils.Swap struct then use this library
* for SwapUtils.Swap struct. Note that this library contains both functions called by users and admins.
* Admin functions should be protected within contracts using this library.
*/
library SwapUtils {
using SafeERC20 for IERC20;
using SafeMath for uint256;
using MathUtils for uint256;
/*** EVENTS ***/
event TokenSwap(
address indexed buyer,
uint256 tokensSold,
uint256 tokensBought,
uint128 soldId,
uint128 boughtId
);
event AddLiquidity(
address indexed provider,
uint256[] tokenAmounts,
uint256[] fees,
uint256 invariant,
uint256 lpTokenSupply
);
event RemoveLiquidity(
address indexed provider,
uint256[] tokenAmounts,
uint256 lpTokenSupply
);
event RemoveLiquidityOne(
address indexed provider,
uint256 lpTokenAmount,
uint256 lpTokenSupply,
uint256 boughtId,
uint256 tokensBought
);
event RemoveLiquidityImbalance(
address indexed provider,
uint256[] tokenAmounts,
uint256[] fees,
uint256 invariant,
uint256 lpTokenSupply
);
event NewAdminFee(uint256 newAdminFee);
event NewSwapFee(uint256 newSwapFee);
struct Swap {
// variables around the ramp management of A,
// the amplification coefficient * n * (n - 1)
// see https://www.curve.fi/stableswap-paper.pdf for details
uint256 initialA;
uint256 futureA;
uint256 initialATime;
uint256 futureATime;
// fee calculation
uint256 swapFee;
uint256 adminFee;
LPToken lpToken;
// contract references for all tokens being pooled
IERC20[] pooledTokens;
// multipliers for each pooled token's precision to get to POOL_PRECISION_DECIMALS
// for example, TBTC has 18 decimals, so the multiplier should be 1. WBTC
// has 8, so the multiplier should be 10 ** 18 / 10 ** 8 => 10 ** 10
uint256[] tokenPrecisionMultipliers;
// the pool balance of each token, in the token's precision
// the contract's actual token balance might differ
uint256[] balances;
}
// Struct storing variables used in calculations in the
// calculateWithdrawOneTokenDY function to avoid stack too deep errors
struct CalculateWithdrawOneTokenDYInfo {
uint256 d0;
uint256 d1;
uint256 newY;
uint256 feePerToken;
uint256 preciseA;
}
// Struct storing variables used in calculations in the
// {add,remove}Liquidity functions to avoid stack too deep errors
struct ManageLiquidityInfo {
uint256 d0;
uint256 d1;
uint256 d2;
uint256 preciseA;
LPToken lpToken;
uint256 totalSupply;
uint256[] balances;
uint256[] multipliers;
}
// the precision all pools tokens will be converted to
uint8 public constant POOL_PRECISION_DECIMALS = 18;
// the denominator used to calculate admin and LP fees. For example, an
// LP fee might be something like tradeAmount.mul(fee).div(FEE_DENOMINATOR)
uint256 private constant FEE_DENOMINATOR = 10**10;
// Max swap fee is 1% or 100bps of each swap
uint256 public constant MAX_SWAP_FEE = 10**8;
// Max adminFee is 100% of the swapFee
// adminFee does not add additional fee on top of swapFee
// Instead it takes a certain % of the swapFee. Therefore it has no impact on the
// users but only on the earnings of LPs
uint256 public constant MAX_ADMIN_FEE = 10**10;
// Constant value used as max loop limit
uint256 private constant MAX_LOOP_LIMIT = 256;
/*** VIEW & PURE FUNCTIONS ***/
function _getAPrecise(Swap storage self) internal view returns (uint256) {
return AmplificationUtils._getAPrecise(self);
}
/**
* @notice Calculate the dy, the amount of selected token that user receives and
* the fee of withdrawing in one token
* @param tokenAmount the amount to withdraw in the pool's precision
* @param tokenIndex which token will be withdrawn
* @param self Swap struct to read from
* @return the amount of token user will receive
*/
function calculateWithdrawOneToken(
Swap storage self,
uint256 tokenAmount,
uint8 tokenIndex
) external view returns (uint256) {
(uint256 availableTokenAmount, ) =
_calculateWithdrawOneToken(
self,
tokenAmount,
tokenIndex,
self.lpToken.totalSupply()
);
return availableTokenAmount;
}
function _calculateWithdrawOneToken(
Swap storage self,
uint256 tokenAmount,
uint8 tokenIndex,
uint256 totalSupply
) internal view returns (uint256, uint256) {
uint256 dy;
uint256 newY;
uint256 currentY;
(dy, newY, currentY) = calculateWithdrawOneTokenDY(
self,
tokenIndex,
tokenAmount,
totalSupply
);
// dy_0 (without fees)
// dy, dy_0 - dy
uint256 dySwapFee =
currentY
.sub(newY)
.div(self.tokenPrecisionMultipliers[tokenIndex])
.sub(dy);
return (dy, dySwapFee);
}
/**
* @notice Calculate the dy of withdrawing in one token
* @param self Swap struct to read from
* @param tokenIndex which token will be withdrawn
* @param tokenAmount the amount to withdraw in the pools precision
* @return the d and the new y after withdrawing one token
*/
function calculateWithdrawOneTokenDY(
Swap storage self,
uint8 tokenIndex,
uint256 tokenAmount,
uint256 totalSupply
)
internal
view
returns (
uint256,
uint256,
uint256
)
{
// Get the current D, then solve the stableswap invariant
// y_i for D - tokenAmount
uint256[] memory xp = _xp(self);
require(tokenIndex < xp.length, "Token index out of range");
CalculateWithdrawOneTokenDYInfo memory v =
CalculateWithdrawOneTokenDYInfo(0, 0, 0, 0, 0);
v.preciseA = _getAPrecise(self);
v.d0 = getD(xp, v.preciseA);
v.d1 = v.d0.sub(tokenAmount.mul(v.d0).div(totalSupply));
require(tokenAmount <= xp[tokenIndex], "Withdraw exceeds available");
v.newY = getYD(v.preciseA, tokenIndex, xp, v.d1);
uint256[] memory xpReduced = new uint256[](xp.length);
v.feePerToken = _feePerToken(self.swapFee, xp.length);
for (uint256 i = 0; i < xp.length; i++) {
uint256 xpi = xp[i];
// if i == tokenIndex, dxExpected = xp[i] * d1 / d0 - newY
// else dxExpected = xp[i] - (xp[i] * d1 / d0)
// xpReduced[i] -= dxExpected * fee / FEE_DENOMINATOR
xpReduced[i] = xpi.sub(
(
(i == tokenIndex)
? xpi.mul(v.d1).div(v.d0).sub(v.newY)
: xpi.sub(xpi.mul(v.d1).div(v.d0))
)
.mul(v.feePerToken)
.div(FEE_DENOMINATOR)
);
}
uint256 dy =
xpReduced[tokenIndex].sub(
getYD(v.preciseA, tokenIndex, xpReduced, v.d1)
);
dy = dy.sub(1).div(self.tokenPrecisionMultipliers[tokenIndex]);
return (dy, v.newY, xp[tokenIndex]);
}
/**
* @notice Calculate the price of a token in the pool with given
* precision-adjusted balances and a particular D.
*
* @dev This is accomplished via solving the invariant iteratively.
* See the StableSwap paper and Curve.fi implementation for further details.
*
* 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)
*
* @param a the amplification coefficient * n * (n - 1). See the StableSwap paper for details.
* @param tokenIndex Index of token we are calculating for.
* @param xp a precision-adjusted set of pool balances. Array should be
* the same cardinality as the pool.
* @param d the stableswap invariant
* @return the price of the token, in the same precision as in xp
*/
function getYD(
uint256 a,
uint8 tokenIndex,
uint256[] memory xp,
uint256 d
) internal pure returns (uint256) {
uint256 numTokens = xp.length;
require(tokenIndex < numTokens, "Token not found");
uint256 c = d;
uint256 s;
uint256 nA = a.mul(numTokens);
for (uint256 i = 0; i < numTokens; i++) {
if (i != tokenIndex) {
s = s.add(xp[i]);
c = c.mul(d).div(xp[i].mul(numTokens));
// If we were to protect the division loss we would have to keep the denominator separate
// and divide at the end. However this leads to overflow with large numTokens or/and D.
// c = c * D * D * D * ... overflow!
}
}
c = c.mul(d).mul(AmplificationUtils.A_PRECISION).div(nA.mul(numTokens));
uint256 b = s.add(d.mul(AmplificationUtils.A_PRECISION).div(nA));
uint256 yPrev;
uint256 y = d;
for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) {
yPrev = y;
y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d));
if (y.within1(yPrev)) {
return y;
}
}
revert("Approximation did not converge");
}
/**
* @notice Get D, the StableSwap invariant, based on a set of balances and a particular A.
* @param xp a precision-adjusted set of pool balances. Array should be the same cardinality
* as the pool.
* @param a the amplification coefficient * n * (n - 1) in A_PRECISION.
* See the StableSwap paper for details
* @return the invariant, at the precision of the pool
*/
function getD(uint256[] memory xp, uint256 a)
internal
pure
returns (uint256)
{
uint256 numTokens = xp.length;
uint256 s;
for (uint256 i = 0; i < numTokens; i++) {
s = s.add(xp[i]);
}
if (s == 0) {
return 0;
}
uint256 prevD;
uint256 d = s;
uint256 nA = a.mul(numTokens);
for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) {
uint256 dP = d;
for (uint256 j = 0; j < numTokens; j++) {
dP = dP.mul(d).div(xp[j].mul(numTokens));
// If we were to protect the division loss we would have to keep the denominator separate
// and divide at the end. However this leads to overflow with large numTokens or/and D.
// dP = dP * D * D * D * ... overflow!
}
prevD = d;
d = nA
.mul(s)
.div(AmplificationUtils.A_PRECISION)
.add(dP.mul(numTokens))
.mul(d)
.div(
nA
.sub(AmplificationUtils.A_PRECISION)
.mul(d)
.div(AmplificationUtils.A_PRECISION)
.add(numTokens.add(1).mul(dP))
);
if (d.within1(prevD)) {
return d;
}
}
// Convergence should occur in 4 loops or less. If this is reached, there may be something wrong
// with the pool. If this were to occur repeatedly, LPs should withdraw via `removeLiquidity()`
// function which does not rely on D.
revert("D does not converge");
}
/**
* @notice Given a set of balances and precision multipliers, return the
* precision-adjusted balances.
*
* @param balances an array of token balances, in their native precisions.
* These should generally correspond with pooled tokens.
*
* @param precisionMultipliers an array of multipliers, corresponding to
* the amounts in the balances array. When multiplied together they
* should yield amounts at the pool's precision.
*
* @return an array of amounts "scaled" to the pool's precision
*/
function _xp(
uint256[] memory balances,
uint256[] memory precisionMultipliers
) internal pure returns (uint256[] memory) {
uint256 numTokens = balances.length;
require(
numTokens == precisionMultipliers.length,
"Balances must match multipliers"
);
uint256[] memory xp = new uint256[](numTokens);
for (uint256 i = 0; i < numTokens; i++) {
xp[i] = balances[i].mul(precisionMultipliers[i]);
}
return xp;
}
/**3
* @notice Return the precision-adjusted balances of all tokens in the pool
* @param self Swap struct to read from
* @return the pool balances "scaled" to the pool's precision, allowing
* them to be more easily compared.
*/
function _xp(Swap storage self) internal view returns (uint256[] memory) {
return _xp(self.balances, self.tokenPrecisionMultipliers);
}
/**
* @notice Get the virtual price, to help calculate profit
* @param self Swap struct to read from
* @return the virtual price, scaled to precision of POOL_PRECISION_DECIMALS
*/
function getVirtualPrice(Swap storage self)
external
view
returns (uint256)
{
uint256 d = getD(_xp(self), _getAPrecise(self));
LPToken lpToken = self.lpToken;
uint256 supply = lpToken.totalSupply();
if (supply > 0) {
return d.mul(10**uint256(POOL_PRECISION_DECIMALS)).div(supply);
}
return 0;
}
/**
* @notice Calculate the new balances of the tokens given the indexes of the token
* that is swapped from (FROM) and the token that is swapped to (TO).
* This function is used as a helper function to calculate how much TO token
* the user should receive on swap.
*
* @param preciseA precise form of amplification coefficient
* @param tokenIndexFrom index of FROM token
* @param tokenIndexTo index of TO token
* @param x the new total amount of FROM token
* @param xp balances of the tokens in the pool
* @return the amount of TO token that should remain in the pool
*/
function getY(
uint256 preciseA,
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 x,
uint256[] memory xp
) internal pure returns (uint256) {
uint256 numTokens = xp.length;
require(
tokenIndexFrom != tokenIndexTo,
"Can't compare token to itself"
);
require(
tokenIndexFrom < numTokens && tokenIndexTo < numTokens,
"Tokens must be in pool"
);
uint256 d = getD(xp, preciseA);
uint256 c = d;
uint256 s;
uint256 nA = numTokens.mul(preciseA);
uint256 _x;
for (uint256 i = 0; i < numTokens; i++) {
if (i == tokenIndexFrom) {
_x = x;
} else if (i != tokenIndexTo) {
_x = xp[i];
} else {
continue;
}
s = s.add(_x);
c = c.mul(d).div(_x.mul(numTokens));
// If we were to protect the division loss we would have to keep the denominator separate
// and divide at the end. However this leads to overflow with large numTokens or/and D.
// c = c * D * D * D * ... overflow!
}
c = c.mul(d).mul(AmplificationUtils.A_PRECISION).div(nA.mul(numTokens));
uint256 b = s.add(d.mul(AmplificationUtils.A_PRECISION).div(nA));
uint256 yPrev;
uint256 y = d;
// iterative approximation
for (uint256 i = 0; i < MAX_LOOP_LIMIT; i++) {
yPrev = y;
y = y.mul(y).add(c).div(y.mul(2).add(b).sub(d));
if (y.within1(yPrev)) {
return y;
}
}
revert("Approximation did not converge");
}
/**
* @notice Externally calculates a swap between two tokens.
* @param self Swap struct to read from
* @param tokenIndexFrom the token to sell
* @param tokenIndexTo the token to buy
* @param dx the number of tokens to sell. If the token charges a fee on transfers,
* use the amount that gets transferred after the fee.
* @return dy the number of tokens the user will get
*/
function calculateSwap(
Swap storage self,
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx
) external view returns (uint256 dy) {
(dy, ) = _calculateSwap(
self,
tokenIndexFrom,
tokenIndexTo,
dx,
self.balances
);
}
/**
* @notice Internally calculates a swap between two tokens.
*
* @dev The caller is expected to transfer the actual amounts (dx and dy)
* using the token contracts.
*
* @param self Swap struct to read from
* @param tokenIndexFrom the token to sell
* @param tokenIndexTo the token to buy
* @param dx the number of tokens to sell. If the token charges a fee on transfers,
* use the amount that gets transferred after the fee.
* @return dy the number of tokens the user will get
* @return dyFee the associated fee
*/
function _calculateSwap(
Swap storage self,
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx,
uint256[] memory balances
) internal view returns (uint256 dy, uint256 dyFee) {
uint256[] memory multipliers = self.tokenPrecisionMultipliers;
uint256[] memory xp = _xp(balances, multipliers);
require(
tokenIndexFrom < xp.length && tokenIndexTo < xp.length,
"Token index out of range"
);
uint256 x = dx.mul(multipliers[tokenIndexFrom]).add(xp[tokenIndexFrom]);
uint256 y =
getY(_getAPrecise(self), tokenIndexFrom, tokenIndexTo, x, xp);
dy = xp[tokenIndexTo].sub(y).sub(1);
dyFee = dy.mul(self.swapFee).div(FEE_DENOMINATOR);
dy = dy.sub(dyFee).div(multipliers[tokenIndexTo]);
}
/**
* @notice A simple method to calculate amount of each underlying
* tokens that is returned upon burning given amount of
* LP tokens
*
* @param amount the amount of LP tokens that would to be burned on
* withdrawal
* @return array of amounts of tokens user will receive
*/
function calculateRemoveLiquidity(Swap storage self, uint256 amount)
external
view
returns (uint256[] memory)
{
return
_calculateRemoveLiquidity(
self.balances,
amount,
self.lpToken.totalSupply()
);
}
function _calculateRemoveLiquidity(
uint256[] memory balances,
uint256 amount,
uint256 totalSupply
) internal pure returns (uint256[] memory) {
require(amount <= totalSupply, "Cannot exceed total supply");
uint256[] memory amounts = new uint256[](balances.length);
for (uint256 i = 0; i < balances.length; i++) {
amounts[i] = balances[i].mul(amount).div(totalSupply);
}
return amounts;
}
/**
* @notice A simple method to calculate prices from deposits or
* withdrawals, excluding fees but including slippage. This is
* helpful as an input into the various "min" parameters on calls
* to fight front-running
*
* @dev This shouldn't be used outside frontends for user estimates.
*
* @param self Swap struct to read from
* @param amounts an array of token amounts to deposit or withdrawal,
* corresponding to pooledTokens. The amount should be in each
* pooled token's native precision. If a token charges a fee on transfers,
* use the amount that gets transferred after the fee.
* @param deposit whether this is a deposit or a withdrawal
* @return if deposit was true, total amount of lp token that will be minted and if
* deposit was false, total amount of lp token that will be burned
*/
function calculateTokenAmount(
Swap storage self,
uint256[] calldata amounts,
bool deposit
) external view returns (uint256) {
uint256 a = _getAPrecise(self);
uint256[] memory balances = self.balances;
uint256[] memory multipliers = self.tokenPrecisionMultipliers;
uint256 d0 = getD(_xp(balances, multipliers), a);
for (uint256 i = 0; i < balances.length; i++) {
if (deposit) {
balances[i] = balances[i].add(amounts[i]);
} else {
balances[i] = balances[i].sub(
amounts[i],
"Cannot withdraw more than available"
);
}
}
uint256 d1 = getD(_xp(balances, multipliers), a);
uint256 totalSupply = self.lpToken.totalSupply();
if (totalSupply == 0) {
return d1; // first depositor take it all
}
if (deposit) {
return d1.sub(d0).mul(totalSupply).div(d0);
} else {
return d0.sub(d1).mul(totalSupply).div(d0);
}
}
/**
* @notice return accumulated amount of admin fees of the token with given index
* @param self Swap struct to read from
* @param index Index of the pooled token
* @return admin balance in the token's precision
*/
function getAdminBalance(Swap storage self, uint256 index)
external
view
returns (uint256)
{
require(index < self.pooledTokens.length, "Token index out of range");
return
self.pooledTokens[index].balanceOf(address(this)).sub(
self.balances[index]
);
}
/**
* @notice internal helper function to calculate fee per token multiplier used in
* swap fee calculations
* @param swapFee swap fee for the tokens
* @param numTokens number of tokens pooled
*/
function _feePerToken(uint256 swapFee, uint256 numTokens)
internal
pure
returns (uint256)
{
return swapFee.mul(numTokens).div(numTokens.sub(1).mul(4));
}
/*** STATE MODIFYING FUNCTIONS ***/
/**
* @notice swap two tokens in the pool
* @param self Swap struct to read from and write to
* @param tokenIndexFrom the token the user wants to sell
* @param tokenIndexTo the token the user wants to buy
* @param dx the amount of tokens the user wants to sell
* @param minDy the min amount the user would like to receive, or revert.
* @return amount of token user received on swap
*/
function swap(
Swap storage self,
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx,
uint256 minDy
) external returns (uint256) {
{
IERC20 tokenFrom = self.pooledTokens[tokenIndexFrom];
require(
dx <= tokenFrom.balanceOf(msg.sender),
"Cannot swap more than you own"
);
// Transfer tokens first to see if a fee was charged on transfer
uint256 beforeBalance = tokenFrom.balanceOf(address(this));
tokenFrom.safeTransferFrom(msg.sender, address(this), dx);
// Use the actual transferred amount for AMM math
dx = tokenFrom.balanceOf(address(this)).sub(beforeBalance);
}
uint256 dy;
uint256 dyFee;
uint256[] memory balances = self.balances;
(dy, dyFee) = _calculateSwap(
self,
tokenIndexFrom,
tokenIndexTo,
dx,
balances
);
require(dy >= minDy, "Swap didn't result in min tokens");
uint256 dyAdminFee =
dyFee.mul(self.adminFee).div(FEE_DENOMINATOR).div(
self.tokenPrecisionMultipliers[tokenIndexTo]
);
self.balances[tokenIndexFrom] = balances[tokenIndexFrom].add(dx);
self.balances[tokenIndexTo] = balances[tokenIndexTo].sub(dy).sub(
dyAdminFee
);
self.pooledTokens[tokenIndexTo].safeTransfer(msg.sender, dy);
emit TokenSwap(msg.sender, dx, dy, tokenIndexFrom, tokenIndexTo);
return dy;
}
/**
* @notice Add liquidity to the pool
* @param self Swap struct to read from and write to
* @param amounts the amounts of each token to add, in their native precision
* @param minToMint the minimum LP tokens adding this amount of liquidity
* should mint, otherwise revert. Handy for front-running mitigation
* allowed addresses. If the pool is not in the guarded launch phase, this parameter will be ignored.
* @return amount of LP token user received
*/
function addLiquidity(
Swap storage self,
uint256[] memory amounts,
uint256 minToMint
) external returns (uint256) {
IERC20[] memory pooledTokens = self.pooledTokens;
require(
amounts.length == pooledTokens.length,
"Amounts must match pooled tokens"
);
// current state
ManageLiquidityInfo memory v =
ManageLiquidityInfo(
0,
0,
0,
_getAPrecise(self),
self.lpToken,
0,
self.balances,
self.tokenPrecisionMultipliers
);
v.totalSupply = v.lpToken.totalSupply();
if (v.totalSupply != 0) {
v.d0 = getD(_xp(v.balances, v.multipliers), v.preciseA);
}
uint256[] memory newBalances = new uint256[](pooledTokens.length);
for (uint256 i = 0; i < pooledTokens.length; i++) {
require(
v.totalSupply != 0 || amounts[i] > 0,
"Must supply all tokens in pool"
);
// Transfer tokens first to see if a fee was charged on transfer
if (amounts[i] != 0) {
uint256 beforeBalance =
pooledTokens[i].balanceOf(address(this));
pooledTokens[i].safeTransferFrom(
msg.sender,
address(this),
amounts[i]
);
// Update the amounts[] with actual transfer amount
amounts[i] = pooledTokens[i].balanceOf(address(this)).sub(
beforeBalance
);
}
newBalances[i] = v.balances[i].add(amounts[i]);
}
// invariant after change
v.d1 = getD(_xp(newBalances, v.multipliers), v.preciseA);
require(v.d1 > v.d0, "D should increase");
// updated to reflect fees and calculate the user's LP tokens
v.d2 = v.d1;
uint256[] memory fees = new uint256[](pooledTokens.length);
if (v.totalSupply != 0) {
uint256 feePerToken =
_feePerToken(self.swapFee, pooledTokens.length);
for (uint256 i = 0; i < pooledTokens.length; i++) {
uint256 idealBalance = v.d1.mul(v.balances[i]).div(v.d0);
fees[i] = feePerToken
.mul(idealBalance.difference(newBalances[i]))
.div(FEE_DENOMINATOR);
self.balances[i] = newBalances[i].sub(
fees[i].mul(self.adminFee).div(FEE_DENOMINATOR)
);
newBalances[i] = newBalances[i].sub(fees[i]);
}
v.d2 = getD(_xp(newBalances, v.multipliers), v.preciseA);
} else {
// the initial depositor doesn't pay fees
self.balances = newBalances;
}
uint256 toMint;
if (v.totalSupply == 0) {
toMint = v.d1;
} else {
toMint = v.d2.sub(v.d0).mul(v.totalSupply).div(v.d0);
}
require(toMint >= minToMint, "Couldn't mint min requested");
// mint the user's LP tokens
v.lpToken.mint(msg.sender, toMint);
emit AddLiquidity(
msg.sender,
amounts,
fees,
v.d1,
v.totalSupply.add(toMint)
);
return toMint;
}
/**
* @notice Burn LP tokens to remove liquidity from the pool.
* @dev Liquidity can always be removed, even when the pool is paused.
* @param self Swap struct to read from and write to
* @param amount the amount of LP tokens to burn
* @param minAmounts the minimum amounts of each token in the pool
* acceptable for this burn. Useful as a front-running mitigation
* @return amounts of tokens the user received
*/
function removeLiquidity(
Swap storage self,
uint256 amount,
uint256[] calldata minAmounts
) external returns (uint256[] memory) {
LPToken lpToken = self.lpToken;
IERC20[] memory pooledTokens = self.pooledTokens;
require(amount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf");
require(
minAmounts.length == pooledTokens.length,
"minAmounts must match poolTokens"
);
uint256[] memory balances = self.balances;
uint256 totalSupply = lpToken.totalSupply();
uint256[] memory amounts =
_calculateRemoveLiquidity(balances, amount, totalSupply);
for (uint256 i = 0; i < amounts.length; i++) {
require(amounts[i] >= minAmounts[i], "amounts[i] < minAmounts[i]");
self.balances[i] = balances[i].sub(amounts[i]);
pooledTokens[i].safeTransfer(msg.sender, amounts[i]);
}
lpToken.burnFrom(msg.sender, amount);
emit RemoveLiquidity(msg.sender, amounts, totalSupply.sub(amount));
return amounts;
}
/**
* @notice Remove liquidity from the pool all in one token.
* @param self Swap struct to read from and write to
* @param tokenAmount the amount of the lp tokens to burn
* @param tokenIndex the index of the token you want to receive
* @param minAmount the minimum amount to withdraw, otherwise revert
* @return amount chosen token that user received
*/
function removeLiquidityOneToken(
Swap storage self,
uint256 tokenAmount,
uint8 tokenIndex,
uint256 minAmount
) external returns (uint256) {
LPToken lpToken = self.lpToken;
IERC20[] memory pooledTokens = self.pooledTokens;
require(tokenAmount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf");
require(tokenIndex < pooledTokens.length, "Token not found");
uint256 totalSupply = lpToken.totalSupply();
(uint256 dy, uint256 dyFee) =
_calculateWithdrawOneToken(
self,
tokenAmount,
tokenIndex,
totalSupply
);
require(dy >= minAmount, "dy < minAmount");
self.balances[tokenIndex] = self.balances[tokenIndex].sub(
dy.add(dyFee.mul(self.adminFee).div(FEE_DENOMINATOR))
);
lpToken.burnFrom(msg.sender, tokenAmount);
pooledTokens[tokenIndex].safeTransfer(msg.sender, dy);
emit RemoveLiquidityOne(
msg.sender,
tokenAmount,
totalSupply,
tokenIndex,
dy
);
return dy;
}
/**
* @notice Remove liquidity from the pool, weighted differently than the
* pool's current balances.
*
* @param self Swap struct to read from and write to
* @param amounts how much of each token to withdraw
* @param maxBurnAmount the max LP token provider is willing to pay to
* remove liquidity. Useful as a front-running mitigation.
* @return actual amount of LP tokens burned in the withdrawal
*/
function removeLiquidityImbalance(
Swap storage self,
uint256[] memory amounts,
uint256 maxBurnAmount
) public returns (uint256) {
ManageLiquidityInfo memory v =
ManageLiquidityInfo(
0,
0,
0,
_getAPrecise(self),
self.lpToken,
0,
self.balances,
self.tokenPrecisionMultipliers
);
v.totalSupply = v.lpToken.totalSupply();
IERC20[] memory pooledTokens = self.pooledTokens;
require(
amounts.length == pooledTokens.length,
"Amounts should match pool tokens"
);
require(
maxBurnAmount <= v.lpToken.balanceOf(msg.sender) &&
maxBurnAmount != 0,
">LP.balanceOf"
);
uint256 feePerToken = _feePerToken(self.swapFee, pooledTokens.length);
uint256[] memory fees = new uint256[](pooledTokens.length);
{
uint256[] memory balances1 = new uint256[](pooledTokens.length);
v.d0 = getD(_xp(v.balances, v.multipliers), v.preciseA);
for (uint256 i = 0; i < pooledTokens.length; i++) {
balances1[i] = v.balances[i].sub(
amounts[i],
"Cannot withdraw more than available"
);
}
v.d1 = getD(_xp(balances1, v.multipliers), v.preciseA);
for (uint256 i = 0; i < pooledTokens.length; i++) {
uint256 idealBalance = v.d1.mul(v.balances[i]).div(v.d0);
uint256 difference = idealBalance.difference(balances1[i]);
fees[i] = feePerToken.mul(difference).div(FEE_DENOMINATOR);
self.balances[i] = balances1[i].sub(
fees[i].mul(self.adminFee).div(FEE_DENOMINATOR)
);
balances1[i] = balances1[i].sub(fees[i]);
}
v.d2 = getD(_xp(balances1, v.multipliers), v.preciseA);
}
uint256 tokenAmount = v.d0.sub(v.d2).mul(v.totalSupply).div(v.d0);
require(tokenAmount != 0, "Burnt amount cannot be zero");
tokenAmount = tokenAmount.add(1);
require(tokenAmount <= maxBurnAmount, "tokenAmount > maxBurnAmount");
v.lpToken.burnFrom(msg.sender, tokenAmount);
for (uint256 i = 0; i < pooledTokens.length; i++) {
pooledTokens[i].safeTransfer(msg.sender, amounts[i]);
}
emit RemoveLiquidityImbalance(
msg.sender,
amounts,
fees,
v.d1,
v.totalSupply.sub(tokenAmount)
);
return tokenAmount;
}
/**
* @notice withdraw all admin fees to a given address
* @param self Swap struct to withdraw fees from
* @param to Address to send the fees to
*/
function withdrawAdminFees(Swap storage self, address to) external {
IERC20[] memory pooledTokens = self.pooledTokens;
for (uint256 i = 0; i < pooledTokens.length; i++) {
IERC20 token = pooledTokens[i];
uint256 balance =
token.balanceOf(address(this)).sub(self.balances[i]);
if (balance != 0) {
token.safeTransfer(to, balance);
}
}
}
/**
* @notice Sets the admin fee
* @dev adminFee cannot be higher than 100% of the swap fee
* @param self Swap struct to update
* @param newAdminFee new admin fee to be applied on future transactions
*/
function setAdminFee(Swap storage self, uint256 newAdminFee) external {
require(newAdminFee <= MAX_ADMIN_FEE, "Fee is too high");
self.adminFee = newAdminFee;
emit NewAdminFee(newAdminFee);
}
/**
* @notice update the swap fee
* @dev fee cannot be higher than 1% of each swap
* @param self Swap struct to update
* @param newSwapFee new swap fee to be applied on future transactions
*/
function setSwapFee(Swap storage self, uint256 newSwapFee) external {
require(newSwapFee <= MAX_SWAP_FEE, "Fee is too high");
self.swapFee = newSwapFee;
emit NewSwapFee(newSwapFee);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "./IERC20.sol";
import "../../math/SafeMath.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 SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
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'
// solhint-disable-next-line max-line-length
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));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @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");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "./SwapUtils.sol";
/**
* @title AmplificationUtils library
* @notice A library to calculate and ramp the A parameter of a given `SwapUtils.Swap` struct.
* This library assumes the struct is fully validated.
*/
library AmplificationUtils {
using SafeMath for uint256;
event RampA(
uint256 oldA,
uint256 newA,
uint256 initialTime,
uint256 futureTime
);
event StopRampA(uint256 currentA, uint256 time);
// Constant values used in ramping A calculations
uint256 public constant A_PRECISION = 100;
uint256 public constant MAX_A = 10**6;
uint256 private constant MAX_A_CHANGE = 2;
uint256 private constant MIN_RAMP_TIME = 7 days;
/**
* @notice Return A, the amplification coefficient * n * (n - 1)
* @dev See the StableSwap paper for details
* @param self Swap struct to read from
* @return A parameter
*/
function getA(SwapUtils.Swap storage self) external view returns (uint256) {
return _getAPrecise(self).div(A_PRECISION);
}
/**
* @notice Return A in its raw precision
* @dev See the StableSwap paper for details
* @param self Swap struct to read from
* @return A parameter in its raw precision form
*/
function getAPrecise(SwapUtils.Swap storage self)
external
view
returns (uint256)
{
return _getAPrecise(self);
}
/**
* @notice Return A in its raw precision
* @dev See the StableSwap paper for details
* @param self Swap struct to read from
* @return A parameter in its raw precision form
*/
function _getAPrecise(SwapUtils.Swap storage self)
internal
view
returns (uint256)
{
uint256 t1 = self.futureATime; // time when ramp is finished
uint256 a1 = self.futureA; // final A value when ramp is finished
if (block.timestamp < t1) {
uint256 t0 = self.initialATime; // time when ramp is started
uint256 a0 = self.initialA; // initial A value when ramp is started
if (a1 > a0) {
// a0 + (a1 - a0) * (block.timestamp - t0) / (t1 - t0)
return
a0.add(
a1.sub(a0).mul(block.timestamp.sub(t0)).div(t1.sub(t0))
);
} else {
// a0 - (a0 - a1) * (block.timestamp - t0) / (t1 - t0)
return
a0.sub(
a0.sub(a1).mul(block.timestamp.sub(t0)).div(t1.sub(t0))
);
}
} else {
return a1;
}
}
/**
* @notice Start ramping up or down A parameter towards given futureA_ and futureTime_
* Checks if the change is too rapid, and commits the new A value only when it falls under
* the limit range.
* @param self Swap struct to update
* @param futureA_ the new A to ramp towards
* @param futureTime_ timestamp when the new A should be reached
*/
function rampA(
SwapUtils.Swap storage self,
uint256 futureA_,
uint256 futureTime_
) external {
require(
block.timestamp >= self.initialATime.add(1 days),
"Wait 1 day before starting ramp"
);
require(
futureTime_ >= block.timestamp.add(MIN_RAMP_TIME),
"Insufficient ramp time"
);
require(
futureA_ > 0 && futureA_ < MAX_A,
"futureA_ must be > 0 and < MAX_A"
);
uint256 initialAPrecise = _getAPrecise(self);
uint256 futureAPrecise = futureA_.mul(A_PRECISION);
if (futureAPrecise < initialAPrecise) {
require(
futureAPrecise.mul(MAX_A_CHANGE) >= initialAPrecise,
"futureA_ is too small"
);
} else {
require(
futureAPrecise <= initialAPrecise.mul(MAX_A_CHANGE),
"futureA_ is too large"
);
}
self.initialA = initialAPrecise;
self.futureA = futureAPrecise;
self.initialATime = block.timestamp;
self.futureATime = futureTime_;
emit RampA(
initialAPrecise,
futureAPrecise,
block.timestamp,
futureTime_
);
}
/**
* @notice Stops ramping A immediately. Once this function is called, rampA()
* cannot be called for another 24 hours
* @param self Swap struct to update
*/
function stopRampA(SwapUtils.Swap storage self) external {
require(self.futureATime > block.timestamp, "Ramp is already stopped");
uint256 currentA = _getAPrecise(self);
self.initialA = currentA;
self.futureA = currentA;
self.initialATime = block.timestamp;
self.futureATime = block.timestamp;
emit StopRampA(currentA, block.timestamp);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/drafts/ERC20PermitUpgradeable.sol";
import "./interfaces/ISwap.sol";
/**
* @title Liquidity Provider Token
* @notice This token is an ERC20 detailed token with added capability to be minted by the owner.
* It is used to represent user's shares when providing liquidity to swap contracts.
* @dev Only Swap contracts should initialize and own LPToken contracts.
*/
contract LPToken is ERC20PermitUpgradeable, ERC20BurnableUpgradeable, OwnableUpgradeable {
using SafeMathUpgradeable for uint256;
/**
* @notice Initializes this LPToken contract with the given name and symbol
* @dev The caller of this function will become the owner. A Swap contract should call this
* in its initializer function.
* @param name name of this token
* @param symbol symbol of this token
*/
function initialize(string memory name, string memory symbol)
external
initializer
returns (bool)
{
__Context_init_unchained();
__ERC20_init_unchained(name, symbol);
__ERC20Permit_init(name);
__Ownable_init_unchained();
return true;
}
/**
* @notice Mints the given amount of LPToken to the recipient.
* @dev only owner can call this mint function
* @param recipient address of account to receive the tokens
* @param amount amount of tokens to mint
*/
function mint(address recipient, uint256 amount) external onlyOwner {
require(amount != 0, "LPToken: cannot mint 0");
_mint(recipient, amount);
}
/**
* @dev Overrides ERC20._beforeTokenTransfer() which get called on every transfers including
* minting and burning. * This assumes the owner is set to a Swap contract's address.
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual override(ERC20Upgradeable) {
super._beforeTokenTransfer(from, to, amount);
require(to != address(this), "LPToken: cannot send to itself");
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "@openzeppelin/contracts/math/SafeMath.sol";
/**
* @title MathUtils library
* @notice A library to be used in conjunction with SafeMath. Contains functions for calculating
* differences between two uint256.
*/
library MathUtils {
/**
* @notice Compares a and b and returns true if the difference between a and b
* is less than 1 or equal to each other.
* @param a uint256 to compare with
* @param b uint256 to compare with
* @return True if the difference between a and b is less than 1 or equal,
* otherwise return false
*/
function within1(uint256 a, uint256 b) internal pure returns (bool) {
return (difference(a, b) <= 1);
}
/**
* @notice Calculates absolute difference between a and b
* @param a uint256 to compare with
* @param b uint256 to compare with
* @return Difference between a and b
*/
function difference(uint256 a, uint256 b) internal pure returns (uint256) {
if (a > b) {
return a - b;
}
return b - a;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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 `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool);
/**
* @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);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
/**
* @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
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 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://diligence.consensys.net/posts/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.5.11/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");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(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 functionCall(target, data, "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");
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(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) {
require(isContract(target), "Address: static call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(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) {
require(isContract(target), "Address: delegate call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
// 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
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../../utils/ContextUpgradeable.sol";
import "./ERC20Upgradeable.sol";
import "../../proxy/Initializable.sol";
/**
* @dev Extension of {ERC20} that allows token holders to destroy both their own
* tokens and those that they have an allowance for, in a way that can be
* recognized off-chain (via event analysis).
*/
abstract contract ERC20BurnableUpgradeable is Initializable, ContextUpgradeable, ERC20Upgradeable {
function __ERC20Burnable_init() internal initializer {
__Context_init_unchained();
__ERC20Burnable_init_unchained();
}
function __ERC20Burnable_init_unchained() internal initializer {
}
using SafeMathUpgradeable for uint256;
/**
* @dev Destroys `amount` tokens from the caller.
*
* See {ERC20-_burn}.
*/
function burn(uint256 amount) public virtual {
_burn(_msgSender(), amount);
}
/**
* @dev Destroys `amount` tokens from `account`, deducting from the caller's
* allowance.
*
* See {ERC20-_burn} and {ERC20-allowance}.
*
* Requirements:
*
* - the caller must have allowance for ``accounts``'s tokens of at least
* `amount`.
*/
function burnFrom(address account, uint256 amount) public virtual {
uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
_approve(account, _msgSender(), decreasedAllowance);
_burn(account, amount);
}
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal initializer {
__Context_init_unchained();
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal initializer {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMathUpgradeable {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.5 <0.8.0;
import "../token/ERC20/ERC20Upgradeable.sol";
import "./IERC20PermitUpgradeable.sol";
import "../cryptography/ECDSAUpgradeable.sol";
import "../utils/CountersUpgradeable.sol";
import "./EIP712Upgradeable.sol";
import "../proxy/Initializable.sol";
/**
* @dev Implementation 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.
*
* _Available since v3.4._
*/
abstract contract ERC20PermitUpgradeable is Initializable, ERC20Upgradeable, IERC20PermitUpgradeable, EIP712Upgradeable {
using CountersUpgradeable for CountersUpgradeable.Counter;
mapping (address => CountersUpgradeable.Counter) private _nonces;
// solhint-disable-next-line var-name-mixedcase
bytes32 private _PERMIT_TYPEHASH;
/**
* @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
*
* It's a good idea to use the same `name` that is defined as the ERC20 token name.
*/
function __ERC20Permit_init(string memory name) internal initializer {
__Context_init_unchained();
__EIP712_init_unchained(name, "1");
__ERC20Permit_init_unchained(name);
}
function __ERC20Permit_init_unchained(string memory name) internal initializer {
_PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
}
/**
* @dev See {IERC20Permit-permit}.
*/
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual override {
// solhint-disable-next-line not-rely-on-time
require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
bytes32 structHash = keccak256(
abi.encode(
_PERMIT_TYPEHASH,
owner,
spender,
value,
_nonces[owner].current(),
deadline
)
);
bytes32 hash = _hashTypedDataV4(structHash);
address signer = ECDSAUpgradeable.recover(hash, v, r, s);
require(signer == owner, "ERC20Permit: invalid signature");
_nonces[owner].increment();
_approve(owner, spender, value);
}
/**
* @dev See {IERC20Permit-nonces}.
*/
function nonces(address owner) public view override returns (uint256) {
return _nonces[owner].current();
}
/**
* @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view override returns (bytes32) {
return _domainSeparatorV4();
}
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
interface ISwap {
// pool data view functions
function getA() external view returns (uint256);
function getToken(uint8 index) external view returns (IERC20);
function getTokenIndex(address tokenAddress) external view returns (uint8);
function getTokenBalance(uint8 index) external view returns (uint256);
function getVirtualPrice() external view returns (uint256);
function getNumberOfTokens() external view returns (uint256);
function getLpToken() external view returns (IERC20);
// min return calculation functions
function calculateSwap(
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx
) external view returns (uint256);
function calculateTokenAmount(uint256[] calldata amounts, bool deposit)
external
view
returns (uint256);
function calculateRemoveLiquidity(uint256 amount)
external
view
returns (uint256[] memory);
function calculateRemoveLiquidityOneToken(
uint256 tokenAmount,
uint8 tokenIndex
) external view returns (uint256 availableTokenAmount);
// state modifying functions
function initialize(
IERC20[] memory pooledTokens,
uint8[] memory decimals,
string memory lpTokenName,
string memory lpTokenSymbol,
uint256 a,
uint256 fee,
uint256 adminFee,
address lpTokenTargetAddress
) external;
function swap(
uint8 tokenIndexFrom,
uint8 tokenIndexTo,
uint256 dx,
uint256 minDy,
uint256 deadline
) external returns (uint256);
function addLiquidity(
uint256[] calldata amounts,
uint256 minToMint,
uint256 deadline
) external returns (uint256);
function removeLiquidity(
uint256 amount,
uint256[] calldata minAmounts,
uint256 deadline
) external returns (uint256[] memory);
function removeLiquidityOneToken(
uint256 tokenAmount,
uint8 tokenIndex,
uint256 minAmount,
uint256 deadline
) external returns (uint256);
function removeLiquidityImbalance(
uint256[] calldata amounts,
uint256 maxBurnAmount,
uint256 deadline
) external returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../proxy/Initializable.sol";
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal initializer {
__Context_init_unchained();
}
function __Context_init_unchained() internal initializer {
}
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../../utils/ContextUpgradeable.sol";
import "./IERC20Upgradeable.sol";
import "../../math/SafeMathUpgradeable.sol";
import "../../proxy/Initializable.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable {
using SafeMathUpgradeable for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for {name} and {symbol}, initializes {decimals} with
* a default value of 18.
*
* To select a different value for {decimals}, use {_setupDecimals}.
*
* All three of these values are immutable: they can only be set once during
* construction.
*/
function __ERC20_init(string memory name_, string memory symbol_) internal initializer {
__Context_init_unchained();
__ERC20_init_unchained(name_, symbol_);
}
function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer {
_name = name_;
_symbol = symbol_;
_decimals = 18;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
* called.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return _decimals;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* Requirements:
*
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Sets {decimals} to a value other than the default one of 18.
*
* WARNING: This function should only be called from the constructor. Most
* applications that interact with token contracts will not expect
* {decimals} to ever change, and may work incorrectly if it does.
*/
function _setupDecimals(uint8 decimals_) internal virtual {
_decimals = decimals_;
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be to transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
uint256[44] private __gap;
}// SPDX-License-Identifier: MIT
// solhint-disable-next-line compiler-version
pragma solidity >=0.4.24 <0.8.0;
import "../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
*/
bool private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Modifier to protect an initializer function from being invoked twice.
*/
modifier initializer() {
require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
/// @dev Returns true if and only if the function is running in the constructor
function _isConstructor() private view returns (bool) {
return !AddressUpgradeable.isContract(address(this));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @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
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 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://diligence.consensys.net/posts/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.5.11/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");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(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 functionCall(target, data, "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");
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(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) {
require(isContract(target), "Address: static call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
// 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
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @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 `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool);
/**
* @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);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <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.
*/
interface IERC20PermitUpgradeable {
/**
* @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].
*/
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for `permit`, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSAUpgradeable {
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
// Check the signature length
if (signature.length != 65) {
revert("ECDSA: invalid signature length");
}
// Divide the signature in r, s and v variables
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return recover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
require(signer != address(0), "ECDSA: invalid signature");
return signer;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* replicates the behavior of the
* https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
* JSON-RPC method.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../math/SafeMathUpgradeable.sol";
/**
* @title Counters
* @author Matt Condon (@shrugs)
* @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
* of elements in a mapping, issuing ERC721 ids, or counting request ids.
*
* Include with `using Counters for Counters.Counter;`
* Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath}
* overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
* directly accessed.
*/
library CountersUpgradeable {
using SafeMathUpgradeable for uint256;
struct Counter {
// This variable should never be directly accessed by users of the library: interactions must be restricted to
// the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
// this feature: see https://github.com/ethereum/solidity/issues/4637
uint256 _value; // default: 0
}
function current(Counter storage counter) internal view returns (uint256) {
return counter._value;
}
function increment(Counter storage counter) internal {
// The {SafeMath} overflow check can be skipped here, see the comment at the top
counter._value += 1;
}
function decrement(Counter storage counter) internal {
counter._value = counter._value.sub(1);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../proxy/Initializable.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* _Available since v3.4._
*/
abstract contract EIP712Upgradeable is Initializable {
/* solhint-disable var-name-mixedcase */
bytes32 private _HASHED_NAME;
bytes32 private _HASHED_VERSION;
bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/* solhint-enable var-name-mixedcase */
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
function __EIP712_init(string memory name, string memory version) internal initializer {
__EIP712_init_unchained(name, version);
}
function __EIP712_init_unchained(string memory name, string memory version) internal initializer {
bytes32 hashedName = keccak256(bytes(name));
bytes32 hashedVersion = keccak256(bytes(version));
_HASHED_NAME = hashedName;
_HASHED_VERSION = hashedVersion;
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
}
function _buildDomainSeparator(bytes32 typeHash, bytes32 name, bytes32 version) private view returns (bytes32) {
return keccak256(
abi.encode(
typeHash,
name,
version,
_getChainId(),
address(this)
)
);
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash));
}
function _getChainId() private view returns (uint256 chainId) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
// solhint-disable-next-line no-inline-assembly
assembly {
chainId := chainid()
}
}
/**
* @dev The hash of the name parameter for the EIP712 domain.
*
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712NameHash() internal virtual view returns (bytes32) {
return _HASHED_NAME;
}
/**
* @dev The hash of the version parameter for the EIP712 domain.
*
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712VersionHash() internal virtual view returns (bytes32) {
return _HASHED_VERSION;
}
uint256[50] private __gap;
}{
"optimizer": {
"enabled": true,
"runs": 999999
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"fees","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"invariant","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"AddLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newAdminFee","type":"uint256"}],"name":"NewAdminFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newSwapFee","type":"uint256"}],"name":"NewSwapFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"RemoveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"fees","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"invariant","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"}],"name":"RemoveLiquidityImbalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"lpTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokenSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"boughtId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensBought","type":"uint256"}],"name":"RemoveLiquidityOne","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokensSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensBought","type":"uint256"},{"indexed":false,"internalType":"uint128","name":"soldId","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"boughtId","type":"uint128"}],"name":"TokenSwap","type":"event"},{"inputs":[],"name":"MAX_ADMIN_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SWAP_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POOL_PRECISION_DECIMALS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6147af610026600b82828239805160001a60731461001957fe5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106101255760003560e01c806371906c2c116100bc578063a5397b221161008b578063e069274211610070578063e0692742146105b0578063e7a4db81146105dc578063f3de03621461061b57610125565b8063a5397b221461055f578063ab3d8544146105a857610125565b806371906c2c1461041d57806373fd6b3e1461043a5780637d048160146104c3578063834b4910146104e657610125565b8063467e186c116100f8578063467e186c146103145780634b23603c14610344578063704672761461037a57806370703e4a146103aa57610125565b80630296ab501461012a57806324c5c7511461014857806340370edf1461019057806341b91c261461025b575b600080fd5b610132610623565b6040805160ff9092168252519081900360200190f35b81801561015457600080fd5b5061018e6004803603604081101561016b57600080fd5b508035906020013573ffffffffffffffffffffffffffffffffffffffff16610628565b005b81801561019c57600080fd5b50610249600480360360608110156101b357600080fd5b813591908101906040810160208201356401000000008111156101d557600080fd5b8201836020820111156101e757600080fd5b8035906020019184602083028401116401000000008311171561020957600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050913592506107ab915050565b60408051918252519081900360200190f35b81801561026757600080fd5b506102496004803603606081101561027e57600080fd5b813591908101906040810160208201356401000000008111156102a057600080fd5b8201836020820111156102b257600080fd5b803590602001918460208302840111640100000000831117156102d457600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550509135925061122f915050565b81801561032057600080fd5b5061018e6004803603604081101561033757600080fd5b5080359060200135611ab5565b6102496004803603608081101561035a57600080fd5b5080359060ff602082013581169160408101359091169060600135611b66565b81801561038657600080fd5b5061018e6004803603604081101561039d57600080fd5b5080359060200135611bd1565b6103cd600480360360408110156103c057600080fd5b5080359060200135611c83565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156104095781810151838201526020016103f1565b505050509050019250505060405180910390f35b6102496004803603602081101561043357600080fd5b5035611d80565b81801561044657600080fd5b506103cd6004803603606081101561045d57600080fd5b81359160208101359181019060608101604082013564010000000081111561048457600080fd5b82018360208201111561049657600080fd5b803590602001918460208302840111640100000000831117156104b857600080fd5b509092509050611e71565b610249600480360360408110156104d957600080fd5b5080359060200135612367565b610249600480360360608110156104fc57600080fd5b8135919081019060408101602082013564010000000081111561051e57600080fd5b82018360208201111561053057600080fd5b8035906020019184602083028401116401000000008311171561055257600080fd5b919350915035151561247d565b81801561056b57600080fd5b50610249600480360360a081101561058257600080fd5b5080359060ff6020820135811691604081013590911690606081013590608001356126f8565b610249612b73565b610249600480360360608110156105c657600080fd5b508035906020810135906040013560ff16612b7b565b8180156105e857600080fd5b50610249600480360360808110156105ff57600080fd5b5080359060208101359060ff6040820135169060600135612c1f565b610249613084565b601281565b60608260070180548060200260200160405190810160405280929190818152602001828054801561068f57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610664575b5050505050905060005b81518110156107a55760008282815181106106b057fe5b6020026020010151905060006107728660090184815481106106ce57fe5b90600052602060002001548373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561074057600080fd5b505afa158015610754573d6000803e3d6000fd5b505050506040513d602081101561076a57600080fd5b50519061308d565b9050801561079b5761079b73ffffffffffffffffffffffffffffffffffffffff83168683613104565b5050600101610699565b50505050565b600060608460070180548060200260200160405190810160405280929190818152602001828054801561081457602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116107e9575b50505050509050805184511461088b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f416d6f756e7473206d757374206d6174636820706f6f6c656420746f6b656e73604482015290519081900360640190fd5b6108936145fb565b6040518061010001604052806000815260200160008152602001600081526020016108bd88613196565b81526020018760060160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600081526020018760090180548060200260200160405190810160405280929190818152602001828054801561095757602002820191906000526020600020905b815481526020019060010190808311610943575b50505050508152602001876008018054806020026020016040519081016040528092919081815260200182805480156109af57602002820191906000526020600020905b81548152602001906001019080831161099b575b50505050508152509050806080015173ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015610a0357600080fd5b505afa158015610a17573d6000803e3d6000fd5b505050506040513d6020811015610a2d57600080fd5b505160a0820181905215610a5d57610a5a610a508260c001518360e001516131a1565b82606001516132b2565b81525b6060825167ffffffffffffffff81118015610a7757600080fd5b50604051908082528060200260200182016040528015610aa1578160200160208202803683370190505b50905060005b8351811015610d525760a0830151151580610ad557506000878281518110610acb57fe5b6020026020010151115b610b4057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4d75737420737570706c7920616c6c20746f6b656e7320696e20706f6f6c0000604482015290519081900360640190fd5b868181518110610b4c57fe5b6020026020010151600014610cf6576000848281518110610b6957fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610bd757600080fd5b505afa158015610beb573d6000803e3d6000fd5b505050506040513d6020811015610c0157600080fd5b50518851909150610c5e90339030908b9086908110610c1c57fe5b6020026020010151888681518110610c3057fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16613438909392919063ffffffff16565b610cdc81868481518110610c6e57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561074057600080fd5b888381518110610ce857fe5b602002602001018181525050505b610d33878281518110610d0557fe5b60200260200101518460c001518381518110610d1d57fe5b60200260200101516134cd90919063ffffffff16565b828281518110610d3f57fe5b6020908102919091010152600101610aa7565b50610d6e610d64828460e001516131a1565b83606001516132b2565b60208301819052825110610de357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f442073686f756c6420696e637265617365000000000000000000000000000000604482015290519081900360640190fd5b60208201516040830152825160609067ffffffffffffffff81118015610e0857600080fd5b50604051908082528060200260200182016040528015610e32578160200160208202803683370190505b5090508260a00151600014610fd0576000610e5289600401548651613541565b905060005b8551811015610fa9576000610e9e8660000151610e988860c001518581518110610e7d57fe5b6020026020010151896020015161355c90919063ffffffff16565b906135cf565b9050610ed96402540be400610e98610ed2888681518110610ebb57fe5b60200260200101518561365090919063ffffffff16565b869061355c565b848381518110610ee557fe5b602002602001018181525050610f49610f276402540be400610e988e60050154888781518110610f1157fe5b602002602001015161355c90919063ffffffff16565b868481518110610f3357fe5b602002602001015161308d90919063ffffffff16565b8b6009018381548110610f5857fe5b9060005260206000200181905550610f89848381518110610f7557fe5b6020026020010151868481518110610f3357fe5b858381518110610f9557fe5b602090810291909101015250600101610e57565b50610fc5610fbb848660e001516131a1565b85606001516132b2565b604085015250610fe7565b8151610fe59060098a01906020850190614656565b505b60008360a001516000141561100157506020830151611029565b835160a085015160408601516110269291610e9891611020908461308d565b9061355c565b90505b8681101561109857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f436f756c646e2774206d696e74206d696e207265717565737465640000000000604482015290519081900360640190fd5b836080015173ffffffffffffffffffffffffffffffffffffffff166340c10f1933836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050600060405180830381600087803b15801561110d57600080fd5b505af1158015611121573d6000803e3d6000fd5b505050503373ffffffffffffffffffffffffffffffffffffffff167f189c623b666b1b45b83d7178f39b8c087cb09774317ca2f53c2d3c3726f222a28984876020015161117b868a60a001516134cd90919063ffffffff16565b604051808060200180602001858152602001848152602001838103835287818151815260200191508051906020019060200280838360005b838110156111cb5781810151838201526020016111b3565b50505050905001838103825286818151815260200191508051906020019060200280838360005b8381101561120a5781810151838201526020016111f2565b50505050905001965050505050505060405180910390a29450505050505b9392505050565b60006112396145fb565b60405180610100016040528060008152602001600081526020016000815260200161126387613196565b81526020018660060160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001866009018054806020026020016040519081016040528092919081815260200182805480156112fd57602002820191906000526020600020905b8154815260200190600101908083116112e9575b505050505081526020018660080180548060200260200160405190810160405280929190818152602001828054801561135557602002820191906000526020600020905b815481526020019060010190808311611341575b50505050508152509050806080015173ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156113a957600080fd5b505afa1580156113bd573d6000803e3d6000fd5b505050506040513d60208110156113d357600080fd5b505160a0820152600785018054604080516020838102820181019092528281526060939092909183018282801561144057602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611415575b5050505050905080518551146114b757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f416d6f756e74732073686f756c64206d6174636820706f6f6c20746f6b656e73604482015290519081900360640190fd5b816080015173ffffffffffffffffffffffffffffffffffffffff166370a08231336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561152257600080fd5b505afa158015611536573d6000803e3d6000fd5b505050506040513d602081101561154c57600080fd5b5051841180159061155c57508315155b6115c757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f3e4c502e62616c616e63654f6600000000000000000000000000000000000000604482015290519081900360640190fd5b60006115d887600401548351613541565b90506060825167ffffffffffffffff811180156115f457600080fd5b5060405190808252806020026020018201604052801561161e578160200160208202803683370190505b5090506060835167ffffffffffffffff8111801561163b57600080fd5b50604051908082528060200260200182016040528015611665578160200160208202803683370190505b50905061168761167d8660c001518760e001516131a1565b86606001516132b2565b855260005b845181101561170b576116ec8982815181106116a457fe5b6020026020010151604051806060016040528060238152602001614757602391398860c0015184815181106116d557fe5b60200260200101516136689092919063ffffffff16565b8282815181106116f857fe5b602090810291909101015260010161168c565b5061171d61167d828760e001516131a1565b602086015260005b845181101561184a5760006117668760000151610e988960c00151858151811061174b57fe5b60200260200101518a6020015161355c90919063ffffffff16565b9050600061179084848151811061177957fe5b60200260200101518361365090919063ffffffff16565b90506117a56402540be400610e98888461355c565b8584815181106117b157fe5b6020026020010181815250506117e96117dd6402540be400610e988f60050154898881518110610f1157fe5b858581518110610f3357fe5b8c60090184815481106117f857fe5b906000526020600020018190555061182985848151811061181557fe5b6020026020010151858581518110610f3357fe5b84848151811061183557fe5b60209081029190910101525050600101611725565b5061185c61167d828760e001516131a1565b60408601819052855160a08701516000935061188292610e98919061102090849061308d565b9050806118f057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4275726e7420616d6f756e742063616e6e6f74206265207a65726f0000000000604482015290519081900360640190fd5b6118fb8160016134cd565b90508681111561196c57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f746f6b656e416d6f756e74203e206d61784275726e416d6f756e740000000000604482015290519081900360640190fd5b846080015173ffffffffffffffffffffffffffffffffffffffff166379cc679033836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050600060405180830381600087803b1580156119e157600080fd5b505af11580156119f5573d6000803e3d6000fd5b5050505060005b8451811015611a5e57611a56338a8381518110611a1557fe5b6020026020010151878481518110611a2957fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166131049092919063ffffffff16565b6001016119fc565b503373ffffffffffffffffffffffffffffffffffffffff167f3631c28b1f9dd213e0319fb167b554d76b6c283a41143eb400a0d1adb1af17558984886020015161117b868b60a0015161308d90919063ffffffff16565b6305f5e100811115611b2857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f46656520697320746f6f20686967680000000000000000000000000000000000604482015290519081900360640190fd5b600482018190556040805182815290517fd88ea5155021c6f8dafa1a741e173f595cdf77ce7c17d43342131d7f06afdfe59181900360200190a15050565b6000611bc78585858589600901805480602002602001604051908101604052809291908181526020018280548015611bbd57602002820191906000526020600020905b815481526020019060010190808311611ba9575b5050505050613719565b5095945050505050565b6402540be400811115611c4557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f46656520697320746f6f20686967680000000000000000000000000000000000604482015290519081900360640190fd5b600582018190556040805182815290517fab599d640ca80cde2b09b128a4154a8dfe608cb80f4c9399c8b954b01fd35f389181900360200190a15050565b6060611d7783600901805480602002602001604051908101604052809291908181526020018280548015611cd657602002820191906000526020600020905b815481526020019060010190808311611cc2575b5050505050838560060160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611d4657600080fd5b505afa158015611d5a573d6000803e3d6000fd5b505050506040513d6020811015611d7057600080fd5b50516138db565b90505b92915050565b600080611d9d611d8f846139d2565b611d9885613196565b6132b2565b905060008360060160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905060008173ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611e1057600080fd5b505afa158015611e24573d6000803e3d6000fd5b505050506040513d6020811015611e3a57600080fd5b505190508015611e6457611e5a81610e9885670de0b6b3a764000061355c565b9350505050611e6c565b600093505050505b919050565b60068401546007850180546040805160208084028201810190925282815260609473ffffffffffffffffffffffffffffffffffffffff169385939192909190830182828015611ef657602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611ecb575b505050505090508173ffffffffffffffffffffffffffffffffffffffff166370a08231336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611f6457600080fd5b505afa158015611f78573d6000803e3d6000fd5b505050506040513d6020811015611f8e57600080fd5b5051861115611ffe57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f3e4c502e62616c616e63654f6600000000000000000000000000000000000000604482015290519081900360640190fd5b8051841461206d57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f6d696e416d6f756e7473206d757374206d6174636820706f6f6c546f6b656e73604482015290519081900360640190fd5b6060876009018054806020026020016040519081016040528092919081815260200182805480156120bd57602002820191906000526020600020905b8154815260200190600101908083116120a9575b5050505050905060008373ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561210c57600080fd5b505afa158015612120573d6000803e3d6000fd5b505050506040513d602081101561213657600080fd5b505190506060612147838a846138db565b905060005b81518110156122415788888281811061216157fe5b9050602002013582828151811061217457fe5b602002602001015110156121e957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f616d6f756e74735b695d203c206d696e416d6f756e74735b695d000000000000604482015290519081900360640190fd5b61220c8282815181106121f857fe5b6020026020010151858381518110610f3357fe5b8b600901828154811061221b57fe5b906000526020600020018190555061223933838381518110611a1557fe5b60010161214c565b50604080517f79cc6790000000000000000000000000000000000000000000000000000000008152336004820152602481018b9052905173ffffffffffffffffffffffffffffffffffffffff8716916379cc679091604480830192600092919082900301818387803b1580156122b657600080fd5b505af11580156122ca573d6000803e3d6000fd5b503392507f88d38ed598fdd809c2bf01ee49cd24b7fdabf379a83d29567952b60324d58cef91508390506122fe858d61308d565b6040518080602001838152602001828103825284818151815260200191508051906020019060200280838360005b8381101561234457818101518382015260200161232c565b50505050905001935050505060405180910390a29450505050505b949350505050565b600782015460009082106123dc57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b611d778360090183815481106123ee57fe5b906000526020600020015484600701848154811061240857fe5b60009182526020918290200154604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff909216926370a0823192602480840193829003018186803b15801561074057600080fd5b60008061248986613196565b90506060866009018054806020026020016040519081016040528092919081815260200182805480156124db57602002820191906000526020600020905b8154815260200190600101908083116124c7575b5050505050905060608760080180548060200260200160405190810160405280929190818152602001828054801561253257602002820191906000526020600020905b81548152602001906001019080831161251e575b50505050509050600061254e61254884846131a1565b856132b2565b905060005b83518110156125fd5786156125a15761258489898381811061257157fe5b90506020020135858381518110610d1d57fe5b84828151811061259057fe5b6020026020010181815250506125f5565b6125dc8989838181106125b057fe5b90506020020135604051806060016040528060238152602001614757602391398684815181106116d557fe5b8482815181106125e857fe5b6020026020010181815250505b600101612553565b50600061261361260d85856131a1565b866132b2565b905060008a60060160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561268157600080fd5b505afa158015612695573d6000803e3d6000fd5b505050506040513d60208110156126ab57600080fd5b50519050806126c15750945061235f9350505050565b87156126e6576126d983610e9883611020868461308d565b965050505050505061235f565b6126d983610e9883611020838761308d565b600080866007018660ff168154811061270d57fe5b60009182526020918290200154604080517f70a08231000000000000000000000000000000000000000000000000000000008152336004820152905173ffffffffffffffffffffffffffffffffffffffff909216935083926370a0823192602480840193829003018186803b15801561278557600080fd5b505afa158015612799573d6000803e3d6000fd5b505050506040513d60208110156127af57600080fd5b505184111561281f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073776170206d6f7265207468616e20796f75206f776e000000604482015290519081900360640190fd5b60008173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561288857600080fd5b505afa15801561289c573d6000803e3d6000fd5b505050506040513d60208110156128b257600080fd5b505190506128d873ffffffffffffffffffffffffffffffffffffffff8316333088613438565b612943818373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561074057600080fd5b9450505060008060608860090180548060200260200160405190810160405280929190818152602001828054801561299a57602002820191906000526020600020905b815481526020019060010190808311612986575b505050505090506129ae8989898985613719565b909350915084831015612a2257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f53776170206469646e277420726573756c7420696e206d696e20746f6b656e73604482015290519081900360640190fd5b6000612a648a6008018960ff1681548110612a3957fe5b9060005260206000200154610e986402540be400610e988e600501548861355c90919063ffffffff16565b9050612a7987838b60ff1681518110610d1d57fe5b8a6009018a60ff1681548110612a8b57fe5b9060005260206000200181905550612ab681612ab086858c60ff1681518110610f3357fe5b9061308d565b8a6009018960ff1681548110612ac857fe5b9060005260206000200181905550612b1633858c6007018b60ff1681548110612aed57fe5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff169190613104565b604080518881526020810186905260ff808c16828401528a166060820152905133917fc6c1e0630dbe9130cc068028486c0d118ddcea348550819defd5cb8c257f8a38919081900360800190a25091925050505b95945050505050565b6305f5e10081565b600080611bc78585858860060160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612bee57600080fd5b505afa158015612c02573d6000803e3d6000fd5b505050506040513d6020811015612c1857600080fd5b5051613a82565b60068401546007850180546040805160208084028201810190925282815260009473ffffffffffffffffffffffffffffffffffffffff16936060939192909190830182828015612ca557602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612c7a575b505050505090508173ffffffffffffffffffffffffffffffffffffffff166370a08231336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015612d1357600080fd5b505afa158015612d27573d6000803e3d6000fd5b505050506040513d6020811015612d3d57600080fd5b5051861115612dad57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f3e4c502e62616c616e63654f6600000000000000000000000000000000000000604482015290519081900360640190fd5b80518560ff1610612e1f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f546f6b656e206e6f7420666f756e640000000000000000000000000000000000604482015290519081900360640190fd5b60008273ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612e6757600080fd5b505afa158015612e7b573d6000803e3d6000fd5b505050506040513d6020811015612e9157600080fd5b50519050600080612ea48a8a8a86613a82565b9150915086821015612f1757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f6479203c206d696e416d6f756e74000000000000000000000000000000000000604482015290519081900360640190fd5b612f6f612f44612f3d6402540be400610e988e600501548661355c90919063ffffffff16565b84906134cd565b8b6009018a60ff1681548110612f5657fe5b906000526020600020015461308d90919063ffffffff16565b8a6009018960ff1681548110612f8157fe5b6000918252602082200191909155604080517f79cc6790000000000000000000000000000000000000000000000000000000008152336004820152602481018c9052905173ffffffffffffffffffffffffffffffffffffffff8816926379cc6790926044808201939182900301818387803b158015612fff57600080fd5b505af1158015613013573d6000803e3d6000fd5b5050505061302b3383868b60ff1681518110611a2957fe5b604080518a81526020810185905260ff8a168183015260608101849052905133917f43fb02998f4e03da2e0e6fff53fdbf0c40a9f45f145dc377fc30615d7d7a8a64919081900360800190a25098975050505050505050565b6402540be40081565b6000828211156130fe57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052613191908490613ae4565b505050565b6000611d7a82613bbc565b8151815160609190811461321657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f42616c616e636573206d757374206d61746368206d756c7469706c6965727300604482015290519081900360640190fd5b60608167ffffffffffffffff8111801561322f57600080fd5b50604051908082528060200260200182016040528015613259578160200160208202803683370190505b50905060005b828110156132a95761328a85828151811061327657fe5b6020026020010151878381518110610f1157fe5b82828151811061329657fe5b602090810291909101015260010161325f565b50949350505050565b815160009081805b828110156132f2576132e88682815181106132d157fe5b6020026020010151836134cd90919063ffffffff16565b91506001016132ba565b508061330357600092505050611d7a565b60008181613311878661355c565b905060005b6101008110156133d0578260005b878110156133535761334961333f898d8481518110610f1157fe5b610e98848861355c565b9150600101613324565b5092935083926133a761338761336e836110208b60016134cd565b6133816064610e98896110208a8461308d565b906134cd565b610e9886611020613398868d61355c565b6133816064610e988b8f61355c565b93506133b38486613c51565b156133c75783975050505050505050611d7a565b50600101613316565b50604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4420646f6573206e6f7420636f6e766572676500000000000000000000000000604482015290519081900360640190fd5b6040805173ffffffffffffffffffffffffffffffffffffffff80861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd000000000000000000000000000000000000000000000000000000001790526107a5908590613ae4565b600082820183811015611d7757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000611d77613556600461102085600161308d565b610e9885855b60008261356b57506000611d7a565b8282028284828161357857fe5b0414611d77576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061470c6021913960400191505060405180910390fd5b600080821161363f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161364857fe5b049392505050565b6000818311156136635750808203611d7a565b500390565b60008184841115613711576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b838110156136d65781810151838201526020016136be565b50505050905090810190601f1680156137035780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b60008060608760080180548060200260200160405190810160405280929190818152602001828054801561376c57602002820191906000526020600020905b815481526020019060010190808311613758575b50505050509050606061377f85836131a1565b905080518860ff16108015613797575080518760ff16105b61380257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b6000613847828a60ff168151811061381657fe5b6020026020010151613381858c60ff168151811061383057fe5b60200260200101518a61355c90919063ffffffff16565b905060006138606138578c613196565b8b8b8587613c68565b905061387a6001612ab083868d60ff1681518110610f3357fe5b955061389c6402540be400610e988d600401548961355c90919063ffffffff16565b94506138cb848a60ff16815181106138b057fe5b6020026020010151610e98878961308d90919063ffffffff16565b9550505050509550959350505050565b60608183111561394c57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f43616e6e6f742065786365656420746f74616c20737570706c79000000000000604482015290519081900360640190fd5b6060845167ffffffffffffffff8111801561396657600080fd5b50604051908082528060200260200182016040528015613990578160200160208202803683370190505b50905060005b85518110156132a9576139b384610e9887898581518110610f1157fe5b8282815181106139bf57fe5b6020908102919091010152600101613996565b6060611d7a82600901805480602002602001604051908101604052809291908181526020018280548015613a2557602002820191906000526020600020905b815481526020019060010190808311613a11575b505050505083600801805480602002602001604051908101604052809291908181526020018280548015613a7857602002820191906000526020600020905b815481526020019060010190808311613a64575b50505050506131a1565b6000806000806000613a9689888a89613efc565b8093508194508295505050506000613ad384612ab08c6008018b60ff1681548110613abd57fe5b600091825260209091200154610e98868861308d565b939a93995092975050505050505050565b6060613b46826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff1661422b9092919063ffffffff16565b80519091501561319157808060200190516020811015613b6557600080fd5b5051613191576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a81526020018061472d602a913960400191505060405180910390fd5b600381015460018201546000919042821115613c48576002840154845480831115613c1a57613c0f613c08613bf1868561308d565b610e98613bfe428761308d565b611020888761308d565b82906134cd565b945050505050611e6c565b613c0f613c41613c2a868561308d565b610e98613c37428761308d565b611020868961308d565b829061308d565b9150611e6c9050565b60006001613c5f8484613650565b11159392505050565b805160009060ff8681169086161415613ce257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e277420636f6d7061726520746f6b656e20746f20697473656c66000000604482015290519081900360640190fd5b808660ff16108015613cf65750808560ff16105b613d6157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f546f6b656e73206d75737420626520696e20706f6f6c00000000000000000000604482015290519081900360640190fd5b6000613d6d84896132b2565b905080600080613d7d858c61355c565b90506000805b86811015613df6578b60ff16811415613d9e57899150613dc8565b8a60ff168114613dc357888181518110613db457fe5b60200260200101519150613dc8565b613dee565b613dd284836134cd565b9350613deb613de1838961355c565b610e98878961355c565b94505b600101613d83565b50613e13613e04838861355c565b610e986064611020888a61355c565b93506000613e30613e2984610e9889606461355c565b85906134cd565b9050600086815b610100811015613e94579091508190613e6a613e5c8a612ab08761338187600261355c565b610e988a613381868061355c565b9150613e768284613c51565b15613e8c57509850612b6a975050505050505050565b600101613e37565b50604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f417070726f78696d6174696f6e20646964206e6f7420636f6e76657267650000604482015290519081900360640190fd5b60008060006060613f0c886139d2565b905080518760ff1610613f8057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b613f886146a1565b6040518060a001604052806000815260200160008152602001600081526020016000815260200160008152509050613fbf89613196565b60808201819052613fd19083906132b2565b808252613ff190613fe9908890610e98908b9061355c565b82519061308d565b60208201528151829060ff8a1690811061400757fe5b602002602001015187111561407d57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5769746864726177206578636565647320617661696c61626c65000000000000604482015290519081900360640190fd5b61409181608001518984846020015161423a565b6040820152815160609067ffffffffffffffff811180156140b157600080fd5b506040519080825280602002602001820160405280156140db578160200160208202803683370190505b5090506140ed8a600401548451613541565b606083015260005b83518110156141a157600084828151811061410c57fe5b60200260200101519050614181613c416402540be400610e9887606001518f60ff16871461415a57885160208a01516141559161414e91610e98908a9061355c565b879061308d565b611020565b6110208960400151612ab08b60000151610e988d602001518b61355c90919063ffffffff16565b83838151811061418d57fe5b6020908102919091010152506001016140f5565b5060006141ca6141bb84608001518c85876020015161423a565b838c60ff1681518110610f3357fe5b90506141f88b6008018b60ff16815481106141e157fe5b600091825260209091200154610e9883600161308d565b9050808360400151858c60ff168151811061420f57fe5b6020026020010151965096509650505050509450945094915050565b606061235f84846000856143ba565b815160009060ff851681116142b057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f546f6b656e206e6f7420666f756e640000000000000000000000000000000000604482015290519081900360640190fd5b826000806142be898561355c565b905060005b84811015614325578860ff16811461431d576142fb8882815181106142e457fe5b6020026020010151846134cd90919063ffffffff16565b925061431a614310868a8481518110610f1157fe5b610e98868a61355c565b93505b6001016142c3565b50614342614333828661355c565b610e986064611020878b61355c565b92506000614358612f3d83610e988a606461355c565b9050600087815b610100811015613e945790915081906143926143848b612ab08761338187600261355c565b610e9889613381868061355c565b915061439e8284613c51565b156143b25750965061235f95505050505050565b60010161435f565b606082471015614415576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806146e66026913960400191505060405180910390fd5b61441e85614575565b61448957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600060608673ffffffffffffffffffffffffffffffffffffffff1685876040518082805190602001908083835b602083106144f357805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016144b6565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114614555576040519150601f19603f3d011682016040523d82523d6000602084013e61455a565b606091505b509150915061456a82828661457b565b979650505050505050565b3b151590565b6060831561458a575081611228565b82511561459a5782518084602001fd5b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526020600482018181528451602484015284518593919283926044019190850190808383600083156136d65781810151838201526020016136be565b60405180610100016040528060008152602001600081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681526020016000815260200160608152602001606081525090565b828054828255906000526020600020908101928215614691579160200282015b82811115614691578251825591602001919060010190614676565b5061469d9291506146d0565b5090565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b5b8082111561469d57600081556001016146d156fe416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656443616e6e6f74207769746864726177206d6f7265207468616e20617661696c61626c65a2646970667358221220f0984c750690f7b31bd0cd2f0d837fa32f174d678a10cfa322128c3f831b51f064736f6c634300060c0033
Deployed Bytecode
0x7320655269bb6995c761da0fb1da0a8d66eb928bed30146080604052600436106101255760003560e01c806371906c2c116100bc578063a5397b221161008b578063e069274211610070578063e0692742146105b0578063e7a4db81146105dc578063f3de03621461061b57610125565b8063a5397b221461055f578063ab3d8544146105a857610125565b806371906c2c1461041d57806373fd6b3e1461043a5780637d048160146104c3578063834b4910146104e657610125565b8063467e186c116100f8578063467e186c146103145780634b23603c14610344578063704672761461037a57806370703e4a146103aa57610125565b80630296ab501461012a57806324c5c7511461014857806340370edf1461019057806341b91c261461025b575b600080fd5b610132610623565b6040805160ff9092168252519081900360200190f35b81801561015457600080fd5b5061018e6004803603604081101561016b57600080fd5b508035906020013573ffffffffffffffffffffffffffffffffffffffff16610628565b005b81801561019c57600080fd5b50610249600480360360608110156101b357600080fd5b813591908101906040810160208201356401000000008111156101d557600080fd5b8201836020820111156101e757600080fd5b8035906020019184602083028401116401000000008311171561020957600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955050913592506107ab915050565b60408051918252519081900360200190f35b81801561026757600080fd5b506102496004803603606081101561027e57600080fd5b813591908101906040810160208201356401000000008111156102a057600080fd5b8201836020820111156102b257600080fd5b803590602001918460208302840111640100000000831117156102d457600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550509135925061122f915050565b81801561032057600080fd5b5061018e6004803603604081101561033757600080fd5b5080359060200135611ab5565b6102496004803603608081101561035a57600080fd5b5080359060ff602082013581169160408101359091169060600135611b66565b81801561038657600080fd5b5061018e6004803603604081101561039d57600080fd5b5080359060200135611bd1565b6103cd600480360360408110156103c057600080fd5b5080359060200135611c83565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156104095781810151838201526020016103f1565b505050509050019250505060405180910390f35b6102496004803603602081101561043357600080fd5b5035611d80565b81801561044657600080fd5b506103cd6004803603606081101561045d57600080fd5b81359160208101359181019060608101604082013564010000000081111561048457600080fd5b82018360208201111561049657600080fd5b803590602001918460208302840111640100000000831117156104b857600080fd5b509092509050611e71565b610249600480360360408110156104d957600080fd5b5080359060200135612367565b610249600480360360608110156104fc57600080fd5b8135919081019060408101602082013564010000000081111561051e57600080fd5b82018360208201111561053057600080fd5b8035906020019184602083028401116401000000008311171561055257600080fd5b919350915035151561247d565b81801561056b57600080fd5b50610249600480360360a081101561058257600080fd5b5080359060ff6020820135811691604081013590911690606081013590608001356126f8565b610249612b73565b610249600480360360608110156105c657600080fd5b508035906020810135906040013560ff16612b7b565b8180156105e857600080fd5b50610249600480360360808110156105ff57600080fd5b5080359060208101359060ff6040820135169060600135612c1f565b610249613084565b601281565b60608260070180548060200260200160405190810160405280929190818152602001828054801561068f57602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311610664575b5050505050905060005b81518110156107a55760008282815181106106b057fe5b6020026020010151905060006107728660090184815481106106ce57fe5b90600052602060002001548373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561074057600080fd5b505afa158015610754573d6000803e3d6000fd5b505050506040513d602081101561076a57600080fd5b50519061308d565b9050801561079b5761079b73ffffffffffffffffffffffffffffffffffffffff83168683613104565b5050600101610699565b50505050565b600060608460070180548060200260200160405190810160405280929190818152602001828054801561081457602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116107e9575b50505050509050805184511461088b57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f416d6f756e7473206d757374206d6174636820706f6f6c656420746f6b656e73604482015290519081900360640190fd5b6108936145fb565b6040518061010001604052806000815260200160008152602001600081526020016108bd88613196565b81526020018760060160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600081526020018760090180548060200260200160405190810160405280929190818152602001828054801561095757602002820191906000526020600020905b815481526020019060010190808311610943575b50505050508152602001876008018054806020026020016040519081016040528092919081815260200182805480156109af57602002820191906000526020600020905b81548152602001906001019080831161099b575b50505050508152509050806080015173ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015610a0357600080fd5b505afa158015610a17573d6000803e3d6000fd5b505050506040513d6020811015610a2d57600080fd5b505160a0820181905215610a5d57610a5a610a508260c001518360e001516131a1565b82606001516132b2565b81525b6060825167ffffffffffffffff81118015610a7757600080fd5b50604051908082528060200260200182016040528015610aa1578160200160208202803683370190505b50905060005b8351811015610d525760a0830151151580610ad557506000878281518110610acb57fe5b6020026020010151115b610b4057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f4d75737420737570706c7920616c6c20746f6b656e7320696e20706f6f6c0000604482015290519081900360640190fd5b868181518110610b4c57fe5b6020026020010151600014610cf6576000848281518110610b6957fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610bd757600080fd5b505afa158015610beb573d6000803e3d6000fd5b505050506040513d6020811015610c0157600080fd5b50518851909150610c5e90339030908b9086908110610c1c57fe5b6020026020010151888681518110610c3057fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16613438909392919063ffffffff16565b610cdc81868481518110610c6e57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561074057600080fd5b888381518110610ce857fe5b602002602001018181525050505b610d33878281518110610d0557fe5b60200260200101518460c001518381518110610d1d57fe5b60200260200101516134cd90919063ffffffff16565b828281518110610d3f57fe5b6020908102919091010152600101610aa7565b50610d6e610d64828460e001516131a1565b83606001516132b2565b60208301819052825110610de357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f442073686f756c6420696e637265617365000000000000000000000000000000604482015290519081900360640190fd5b60208201516040830152825160609067ffffffffffffffff81118015610e0857600080fd5b50604051908082528060200260200182016040528015610e32578160200160208202803683370190505b5090508260a00151600014610fd0576000610e5289600401548651613541565b905060005b8551811015610fa9576000610e9e8660000151610e988860c001518581518110610e7d57fe5b6020026020010151896020015161355c90919063ffffffff16565b906135cf565b9050610ed96402540be400610e98610ed2888681518110610ebb57fe5b60200260200101518561365090919063ffffffff16565b869061355c565b848381518110610ee557fe5b602002602001018181525050610f49610f276402540be400610e988e60050154888781518110610f1157fe5b602002602001015161355c90919063ffffffff16565b868481518110610f3357fe5b602002602001015161308d90919063ffffffff16565b8b6009018381548110610f5857fe5b9060005260206000200181905550610f89848381518110610f7557fe5b6020026020010151868481518110610f3357fe5b858381518110610f9557fe5b602090810291909101015250600101610e57565b50610fc5610fbb848660e001516131a1565b85606001516132b2565b604085015250610fe7565b8151610fe59060098a01906020850190614656565b505b60008360a001516000141561100157506020830151611029565b835160a085015160408601516110269291610e9891611020908461308d565b9061355c565b90505b8681101561109857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f436f756c646e2774206d696e74206d696e207265717565737465640000000000604482015290519081900360640190fd5b836080015173ffffffffffffffffffffffffffffffffffffffff166340c10f1933836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050600060405180830381600087803b15801561110d57600080fd5b505af1158015611121573d6000803e3d6000fd5b505050503373ffffffffffffffffffffffffffffffffffffffff167f189c623b666b1b45b83d7178f39b8c087cb09774317ca2f53c2d3c3726f222a28984876020015161117b868a60a001516134cd90919063ffffffff16565b604051808060200180602001858152602001848152602001838103835287818151815260200191508051906020019060200280838360005b838110156111cb5781810151838201526020016111b3565b50505050905001838103825286818151815260200191508051906020019060200280838360005b8381101561120a5781810151838201526020016111f2565b50505050905001965050505050505060405180910390a29450505050505b9392505050565b60006112396145fb565b60405180610100016040528060008152602001600081526020016000815260200161126387613196565b81526020018660060160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001866009018054806020026020016040519081016040528092919081815260200182805480156112fd57602002820191906000526020600020905b8154815260200190600101908083116112e9575b505050505081526020018660080180548060200260200160405190810160405280929190818152602001828054801561135557602002820191906000526020600020905b815481526020019060010190808311611341575b50505050508152509050806080015173ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156113a957600080fd5b505afa1580156113bd573d6000803e3d6000fd5b505050506040513d60208110156113d357600080fd5b505160a0820152600785018054604080516020838102820181019092528281526060939092909183018282801561144057602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611415575b5050505050905080518551146114b757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f416d6f756e74732073686f756c64206d6174636820706f6f6c20746f6b656e73604482015290519081900360640190fd5b816080015173ffffffffffffffffffffffffffffffffffffffff166370a08231336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561152257600080fd5b505afa158015611536573d6000803e3d6000fd5b505050506040513d602081101561154c57600080fd5b5051841180159061155c57508315155b6115c757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f3e4c502e62616c616e63654f6600000000000000000000000000000000000000604482015290519081900360640190fd5b60006115d887600401548351613541565b90506060825167ffffffffffffffff811180156115f457600080fd5b5060405190808252806020026020018201604052801561161e578160200160208202803683370190505b5090506060835167ffffffffffffffff8111801561163b57600080fd5b50604051908082528060200260200182016040528015611665578160200160208202803683370190505b50905061168761167d8660c001518760e001516131a1565b86606001516132b2565b855260005b845181101561170b576116ec8982815181106116a457fe5b6020026020010151604051806060016040528060238152602001614757602391398860c0015184815181106116d557fe5b60200260200101516136689092919063ffffffff16565b8282815181106116f857fe5b602090810291909101015260010161168c565b5061171d61167d828760e001516131a1565b602086015260005b845181101561184a5760006117668760000151610e988960c00151858151811061174b57fe5b60200260200101518a6020015161355c90919063ffffffff16565b9050600061179084848151811061177957fe5b60200260200101518361365090919063ffffffff16565b90506117a56402540be400610e98888461355c565b8584815181106117b157fe5b6020026020010181815250506117e96117dd6402540be400610e988f60050154898881518110610f1157fe5b858581518110610f3357fe5b8c60090184815481106117f857fe5b906000526020600020018190555061182985848151811061181557fe5b6020026020010151858581518110610f3357fe5b84848151811061183557fe5b60209081029190910101525050600101611725565b5061185c61167d828760e001516131a1565b60408601819052855160a08701516000935061188292610e98919061102090849061308d565b9050806118f057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4275726e7420616d6f756e742063616e6e6f74206265207a65726f0000000000604482015290519081900360640190fd5b6118fb8160016134cd565b90508681111561196c57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f746f6b656e416d6f756e74203e206d61784275726e416d6f756e740000000000604482015290519081900360640190fd5b846080015173ffffffffffffffffffffffffffffffffffffffff166379cc679033836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050600060405180830381600087803b1580156119e157600080fd5b505af11580156119f5573d6000803e3d6000fd5b5050505060005b8451811015611a5e57611a56338a8381518110611a1557fe5b6020026020010151878481518110611a2957fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166131049092919063ffffffff16565b6001016119fc565b503373ffffffffffffffffffffffffffffffffffffffff167f3631c28b1f9dd213e0319fb167b554d76b6c283a41143eb400a0d1adb1af17558984886020015161117b868b60a0015161308d90919063ffffffff16565b6305f5e100811115611b2857604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f46656520697320746f6f20686967680000000000000000000000000000000000604482015290519081900360640190fd5b600482018190556040805182815290517fd88ea5155021c6f8dafa1a741e173f595cdf77ce7c17d43342131d7f06afdfe59181900360200190a15050565b6000611bc78585858589600901805480602002602001604051908101604052809291908181526020018280548015611bbd57602002820191906000526020600020905b815481526020019060010190808311611ba9575b5050505050613719565b5095945050505050565b6402540be400811115611c4557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f46656520697320746f6f20686967680000000000000000000000000000000000604482015290519081900360640190fd5b600582018190556040805182815290517fab599d640ca80cde2b09b128a4154a8dfe608cb80f4c9399c8b954b01fd35f389181900360200190a15050565b6060611d7783600901805480602002602001604051908101604052809291908181526020018280548015611cd657602002820191906000526020600020905b815481526020019060010190808311611cc2575b5050505050838560060160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611d4657600080fd5b505afa158015611d5a573d6000803e3d6000fd5b505050506040513d6020811015611d7057600080fd5b50516138db565b90505b92915050565b600080611d9d611d8f846139d2565b611d9885613196565b6132b2565b905060008360060160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905060008173ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611e1057600080fd5b505afa158015611e24573d6000803e3d6000fd5b505050506040513d6020811015611e3a57600080fd5b505190508015611e6457611e5a81610e9885670de0b6b3a764000061355c565b9350505050611e6c565b600093505050505b919050565b60068401546007850180546040805160208084028201810190925282815260609473ffffffffffffffffffffffffffffffffffffffff169385939192909190830182828015611ef657602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311611ecb575b505050505090508173ffffffffffffffffffffffffffffffffffffffff166370a08231336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611f6457600080fd5b505afa158015611f78573d6000803e3d6000fd5b505050506040513d6020811015611f8e57600080fd5b5051861115611ffe57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f3e4c502e62616c616e63654f6600000000000000000000000000000000000000604482015290519081900360640190fd5b8051841461206d57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f6d696e416d6f756e7473206d757374206d6174636820706f6f6c546f6b656e73604482015290519081900360640190fd5b6060876009018054806020026020016040519081016040528092919081815260200182805480156120bd57602002820191906000526020600020905b8154815260200190600101908083116120a9575b5050505050905060008373ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561210c57600080fd5b505afa158015612120573d6000803e3d6000fd5b505050506040513d602081101561213657600080fd5b505190506060612147838a846138db565b905060005b81518110156122415788888281811061216157fe5b9050602002013582828151811061217457fe5b602002602001015110156121e957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f616d6f756e74735b695d203c206d696e416d6f756e74735b695d000000000000604482015290519081900360640190fd5b61220c8282815181106121f857fe5b6020026020010151858381518110610f3357fe5b8b600901828154811061221b57fe5b906000526020600020018190555061223933838381518110611a1557fe5b60010161214c565b50604080517f79cc6790000000000000000000000000000000000000000000000000000000008152336004820152602481018b9052905173ffffffffffffffffffffffffffffffffffffffff8716916379cc679091604480830192600092919082900301818387803b1580156122b657600080fd5b505af11580156122ca573d6000803e3d6000fd5b503392507f88d38ed598fdd809c2bf01ee49cd24b7fdabf379a83d29567952b60324d58cef91508390506122fe858d61308d565b6040518080602001838152602001828103825284818151815260200191508051906020019060200280838360005b8381101561234457818101518382015260200161232c565b50505050905001935050505060405180910390a29450505050505b949350505050565b600782015460009082106123dc57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b611d778360090183815481106123ee57fe5b906000526020600020015484600701848154811061240857fe5b60009182526020918290200154604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905173ffffffffffffffffffffffffffffffffffffffff909216926370a0823192602480840193829003018186803b15801561074057600080fd5b60008061248986613196565b90506060866009018054806020026020016040519081016040528092919081815260200182805480156124db57602002820191906000526020600020905b8154815260200190600101908083116124c7575b5050505050905060608760080180548060200260200160405190810160405280929190818152602001828054801561253257602002820191906000526020600020905b81548152602001906001019080831161251e575b50505050509050600061254e61254884846131a1565b856132b2565b905060005b83518110156125fd5786156125a15761258489898381811061257157fe5b90506020020135858381518110610d1d57fe5b84828151811061259057fe5b6020026020010181815250506125f5565b6125dc8989838181106125b057fe5b90506020020135604051806060016040528060238152602001614757602391398684815181106116d557fe5b8482815181106125e857fe5b6020026020010181815250505b600101612553565b50600061261361260d85856131a1565b866132b2565b905060008a60060160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561268157600080fd5b505afa158015612695573d6000803e3d6000fd5b505050506040513d60208110156126ab57600080fd5b50519050806126c15750945061235f9350505050565b87156126e6576126d983610e9883611020868461308d565b965050505050505061235f565b6126d983610e9883611020838761308d565b600080866007018660ff168154811061270d57fe5b60009182526020918290200154604080517f70a08231000000000000000000000000000000000000000000000000000000008152336004820152905173ffffffffffffffffffffffffffffffffffffffff909216935083926370a0823192602480840193829003018186803b15801561278557600080fd5b505afa158015612799573d6000803e3d6000fd5b505050506040513d60208110156127af57600080fd5b505184111561281f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f742073776170206d6f7265207468616e20796f75206f776e000000604482015290519081900360640190fd5b60008173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561288857600080fd5b505afa15801561289c573d6000803e3d6000fd5b505050506040513d60208110156128b257600080fd5b505190506128d873ffffffffffffffffffffffffffffffffffffffff8316333088613438565b612943818373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561074057600080fd5b9450505060008060608860090180548060200260200160405190810160405280929190818152602001828054801561299a57602002820191906000526020600020905b815481526020019060010190808311612986575b505050505090506129ae8989898985613719565b909350915084831015612a2257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f53776170206469646e277420726573756c7420696e206d696e20746f6b656e73604482015290519081900360640190fd5b6000612a648a6008018960ff1681548110612a3957fe5b9060005260206000200154610e986402540be400610e988e600501548861355c90919063ffffffff16565b9050612a7987838b60ff1681518110610d1d57fe5b8a6009018a60ff1681548110612a8b57fe5b9060005260206000200181905550612ab681612ab086858c60ff1681518110610f3357fe5b9061308d565b8a6009018960ff1681548110612ac857fe5b9060005260206000200181905550612b1633858c6007018b60ff1681548110612aed57fe5b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff169190613104565b604080518881526020810186905260ff808c16828401528a166060820152905133917fc6c1e0630dbe9130cc068028486c0d118ddcea348550819defd5cb8c257f8a38919081900360800190a25091925050505b95945050505050565b6305f5e10081565b600080611bc78585858860060160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612bee57600080fd5b505afa158015612c02573d6000803e3d6000fd5b505050506040513d6020811015612c1857600080fd5b5051613a82565b60068401546007850180546040805160208084028201810190925282815260009473ffffffffffffffffffffffffffffffffffffffff16936060939192909190830182828015612ca557602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff168152600190910190602001808311612c7a575b505050505090508173ffffffffffffffffffffffffffffffffffffffff166370a08231336040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015612d1357600080fd5b505afa158015612d27573d6000803e3d6000fd5b505050506040513d6020811015612d3d57600080fd5b5051861115612dad57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f3e4c502e62616c616e63654f6600000000000000000000000000000000000000604482015290519081900360640190fd5b80518560ff1610612e1f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f546f6b656e206e6f7420666f756e640000000000000000000000000000000000604482015290519081900360640190fd5b60008273ffffffffffffffffffffffffffffffffffffffff166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015612e6757600080fd5b505afa158015612e7b573d6000803e3d6000fd5b505050506040513d6020811015612e9157600080fd5b50519050600080612ea48a8a8a86613a82565b9150915086821015612f1757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f6479203c206d696e416d6f756e74000000000000000000000000000000000000604482015290519081900360640190fd5b612f6f612f44612f3d6402540be400610e988e600501548661355c90919063ffffffff16565b84906134cd565b8b6009018a60ff1681548110612f5657fe5b906000526020600020015461308d90919063ffffffff16565b8a6009018960ff1681548110612f8157fe5b6000918252602082200191909155604080517f79cc6790000000000000000000000000000000000000000000000000000000008152336004820152602481018c9052905173ffffffffffffffffffffffffffffffffffffffff8816926379cc6790926044808201939182900301818387803b158015612fff57600080fd5b505af1158015613013573d6000803e3d6000fd5b5050505061302b3383868b60ff1681518110611a2957fe5b604080518a81526020810185905260ff8a168183015260608101849052905133917f43fb02998f4e03da2e0e6fff53fdbf0c40a9f45f145dc377fc30615d7d7a8a64919081900360800190a25098975050505050505050565b6402540be40081565b6000828211156130fe57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b6040805173ffffffffffffffffffffffffffffffffffffffff8416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052613191908490613ae4565b505050565b6000611d7a82613bbc565b8151815160609190811461321657604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f42616c616e636573206d757374206d61746368206d756c7469706c6965727300604482015290519081900360640190fd5b60608167ffffffffffffffff8111801561322f57600080fd5b50604051908082528060200260200182016040528015613259578160200160208202803683370190505b50905060005b828110156132a95761328a85828151811061327657fe5b6020026020010151878381518110610f1157fe5b82828151811061329657fe5b602090810291909101015260010161325f565b50949350505050565b815160009081805b828110156132f2576132e88682815181106132d157fe5b6020026020010151836134cd90919063ffffffff16565b91506001016132ba565b508061330357600092505050611d7a565b60008181613311878661355c565b905060005b6101008110156133d0578260005b878110156133535761334961333f898d8481518110610f1157fe5b610e98848861355c565b9150600101613324565b5092935083926133a761338761336e836110208b60016134cd565b6133816064610e98896110208a8461308d565b906134cd565b610e9886611020613398868d61355c565b6133816064610e988b8f61355c565b93506133b38486613c51565b156133c75783975050505050505050611d7a565b50600101613316565b50604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4420646f6573206e6f7420636f6e766572676500000000000000000000000000604482015290519081900360640190fd5b6040805173ffffffffffffffffffffffffffffffffffffffff80861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd000000000000000000000000000000000000000000000000000000001790526107a5908590613ae4565b600082820183811015611d7757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000611d77613556600461102085600161308d565b610e9885855b60008261356b57506000611d7a565b8282028284828161357857fe5b0414611d77576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061470c6021913960400191505060405180910390fd5b600080821161363f57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161364857fe5b049392505050565b6000818311156136635750808203611d7a565b500390565b60008184841115613711576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b838110156136d65781810151838201526020016136be565b50505050905090810190601f1680156137035780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b60008060608760080180548060200260200160405190810160405280929190818152602001828054801561376c57602002820191906000526020600020905b815481526020019060010190808311613758575b50505050509050606061377f85836131a1565b905080518860ff16108015613797575080518760ff16105b61380257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b6000613847828a60ff168151811061381657fe5b6020026020010151613381858c60ff168151811061383057fe5b60200260200101518a61355c90919063ffffffff16565b905060006138606138578c613196565b8b8b8587613c68565b905061387a6001612ab083868d60ff1681518110610f3357fe5b955061389c6402540be400610e988d600401548961355c90919063ffffffff16565b94506138cb848a60ff16815181106138b057fe5b6020026020010151610e98878961308d90919063ffffffff16565b9550505050509550959350505050565b60608183111561394c57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f43616e6e6f742065786365656420746f74616c20737570706c79000000000000604482015290519081900360640190fd5b6060845167ffffffffffffffff8111801561396657600080fd5b50604051908082528060200260200182016040528015613990578160200160208202803683370190505b50905060005b85518110156132a9576139b384610e9887898581518110610f1157fe5b8282815181106139bf57fe5b6020908102919091010152600101613996565b6060611d7a82600901805480602002602001604051908101604052809291908181526020018280548015613a2557602002820191906000526020600020905b815481526020019060010190808311613a11575b505050505083600801805480602002602001604051908101604052809291908181526020018280548015613a7857602002820191906000526020600020905b815481526020019060010190808311613a64575b50505050506131a1565b6000806000806000613a9689888a89613efc565b8093508194508295505050506000613ad384612ab08c6008018b60ff1681548110613abd57fe5b600091825260209091200154610e98868861308d565b939a93995092975050505050505050565b6060613b46826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff1661422b9092919063ffffffff16565b80519091501561319157808060200190516020811015613b6557600080fd5b5051613191576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a81526020018061472d602a913960400191505060405180910390fd5b600381015460018201546000919042821115613c48576002840154845480831115613c1a57613c0f613c08613bf1868561308d565b610e98613bfe428761308d565b611020888761308d565b82906134cd565b945050505050611e6c565b613c0f613c41613c2a868561308d565b610e98613c37428761308d565b611020868961308d565b829061308d565b9150611e6c9050565b60006001613c5f8484613650565b11159392505050565b805160009060ff8681169086161415613ce257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e277420636f6d7061726520746f6b656e20746f20697473656c66000000604482015290519081900360640190fd5b808660ff16108015613cf65750808560ff16105b613d6157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f546f6b656e73206d75737420626520696e20706f6f6c00000000000000000000604482015290519081900360640190fd5b6000613d6d84896132b2565b905080600080613d7d858c61355c565b90506000805b86811015613df6578b60ff16811415613d9e57899150613dc8565b8a60ff168114613dc357888181518110613db457fe5b60200260200101519150613dc8565b613dee565b613dd284836134cd565b9350613deb613de1838961355c565b610e98878961355c565b94505b600101613d83565b50613e13613e04838861355c565b610e986064611020888a61355c565b93506000613e30613e2984610e9889606461355c565b85906134cd565b9050600086815b610100811015613e94579091508190613e6a613e5c8a612ab08761338187600261355c565b610e988a613381868061355c565b9150613e768284613c51565b15613e8c57509850612b6a975050505050505050565b600101613e37565b50604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f417070726f78696d6174696f6e20646964206e6f7420636f6e76657267650000604482015290519081900360640190fd5b60008060006060613f0c886139d2565b905080518760ff1610613f8057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f546f6b656e20696e646578206f7574206f662072616e67650000000000000000604482015290519081900360640190fd5b613f886146a1565b6040518060a001604052806000815260200160008152602001600081526020016000815260200160008152509050613fbf89613196565b60808201819052613fd19083906132b2565b808252613ff190613fe9908890610e98908b9061355c565b82519061308d565b60208201528151829060ff8a1690811061400757fe5b602002602001015187111561407d57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5769746864726177206578636565647320617661696c61626c65000000000000604482015290519081900360640190fd5b61409181608001518984846020015161423a565b6040820152815160609067ffffffffffffffff811180156140b157600080fd5b506040519080825280602002602001820160405280156140db578160200160208202803683370190505b5090506140ed8a600401548451613541565b606083015260005b83518110156141a157600084828151811061410c57fe5b60200260200101519050614181613c416402540be400610e9887606001518f60ff16871461415a57885160208a01516141559161414e91610e98908a9061355c565b879061308d565b611020565b6110208960400151612ab08b60000151610e988d602001518b61355c90919063ffffffff16565b83838151811061418d57fe5b6020908102919091010152506001016140f5565b5060006141ca6141bb84608001518c85876020015161423a565b838c60ff1681518110610f3357fe5b90506141f88b6008018b60ff16815481106141e157fe5b600091825260209091200154610e9883600161308d565b9050808360400151858c60ff168151811061420f57fe5b6020026020010151965096509650505050509450945094915050565b606061235f84846000856143ba565b815160009060ff851681116142b057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f546f6b656e206e6f7420666f756e640000000000000000000000000000000000604482015290519081900360640190fd5b826000806142be898561355c565b905060005b84811015614325578860ff16811461431d576142fb8882815181106142e457fe5b6020026020010151846134cd90919063ffffffff16565b925061431a614310868a8481518110610f1157fe5b610e98868a61355c565b93505b6001016142c3565b50614342614333828661355c565b610e986064611020878b61355c565b92506000614358612f3d83610e988a606461355c565b9050600087815b610100811015613e945790915081906143926143848b612ab08761338187600261355c565b610e9889613381868061355c565b915061439e8284613c51565b156143b25750965061235f95505050505050565b60010161435f565b606082471015614415576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806146e66026913960400191505060405180910390fd5b61441e85614575565b61448957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600060608673ffffffffffffffffffffffffffffffffffffffff1685876040518082805190602001908083835b602083106144f357805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016144b6565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114614555576040519150601f19603f3d011682016040523d82523d6000602084013e61455a565b606091505b509150915061456a82828661457b565b979650505050505050565b3b151590565b6060831561458a575081611228565b82511561459a5782518084602001fd5b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526020600482018181528451602484015284518593919283926044019190850190808383600083156136d65781810151838201526020016136be565b60405180610100016040528060008152602001600081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681526020016000815260200160608152602001606081525090565b828054828255906000526020600020908101928215614691579160200282015b82811115614691578251825591602001919060010190614676565b5061469d9291506146d0565b5090565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b5b8082111561469d57600081556001016146d156fe416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f74207375636365656443616e6e6f74207769746864726177206d6f7265207468616e20617661696c61626c65a2646970667358221220f0984c750690f7b31bd0cd2f0d837fa32f174d678a10cfa322128c3f831b51f064736f6c634300060c0033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in GLMR
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.